Objective-C特性:Runtime
轉載來自:http://www.jianshu.com/p/25a319aee33d
Objective-C是基於C語言加入了面向物件特性和訊息轉發機制的動態語言,這意味著它不僅需要一個編譯器,還需要Runtime系統來動態建立類和物件,進行訊息傳送和轉發。下面通過分析Apple開源的Runtime程式碼(我使用的版本是objc4-646.tar)來深入理解Objective-C的Runtime機制。
Runtime資料結構
在Objective-C中,使用[receiver message]
語法並不會馬上執行receiver
物件的message
方法的程式碼,而是向receiver
傳送一條message
receiver
來處理,也可能由轉發給其他物件來處理,也有可能假裝沒有接收到這條訊息而沒有處理。其實[receiver
message]
被編譯器轉化為:
id objc_msgSend ( id self, SEL op, ... );
下面從兩個資料結構id
和SEL
來逐步分析和理解Runtime有哪些重要的資料結構。
SEL
SEL
是函式objc_msgSend
第二個引數的資料型別,表示方法選擇器,按下面路徑開啟objc.h
檔案
SEL Data Structure
檢視到SEL
資料結構如下:
typedef struct objc_selector *SEL;
其實它就是對映到方法的C字串,你可以通過Objc編譯器命令@selector()
或者Runtime系統的sel_registerName
函式來獲取一個SEL
型別的方法選擇器。
如果你知道selector對應的方法名是什麼,可以通過NSString* NSStringFromSelector(SEL
aSelector)
方法將SEL轉化為字串,再用NSLog
列印。
id
接下來看objc_msgSend
第一個引數的資料型別id
,id
是通用型別指標,能夠表示任何物件。按下面路徑開啟objc.h
檔案
id Data Structure.png
檢視到id
資料結構如下:
/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
id
其實就是一個指向objc_object
結構體指標,它包含一個Class
isa
成員,根據isa
指標就可以順藤摸瓜找到物件所屬的類。
注意:根據Apple的官方文件Key-Value Observing Implementation Details提及,key-value observing是使用
isa-swizzling
的技術實現的,isa
指標在執行時被修改,指向一箇中間類而不是真正的類。所以,你不應該使用isa
指標來確定類的關係,而是使用class方法來確定例項物件的類。
Class
isa
指標的資料型別是Class
,Class
表示物件所屬的類,按下面路徑開啟objc.h
檔案
Class Data Structure
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
可以檢視到Class
其實就是一個objc_class
結構體指標,但這個標頭檔案找不到它的定義,需要在runtime.h
才能找到objc_class
結構體的定義。
按下面路徑開啟runtime.h
檔案
objc_class Data Structure
檢視到objc_class
結構體定義如下:
struct objc_class {
Class isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
注意:
OBJC2_UNAVAILABLE
是一個Apple對Objc系統執行版本進行約束的巨集定義,主要為了相容非Objective-C 2.0的遺留版本,但我們仍能從中獲取一些有用資訊。
讓我們分析一些重要的成員變量表示什麼意思和對應使用哪些資料結構。
-
isa
表示一個Class物件的Class,也就是Meta Class。在面向物件設計中,一切都是物件,Class在設計中本身也是一個物件。我們會在objc-runtime-new.h
檔案找到證據,發現objc_class
有以下定義:struct objc_class : objc_object { // Class ISA; Class superclass; cache_t cache; // formerly cache pointer and vtable class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags ...... }
由此可見,結構體
objc_class
也是繼承objc_object
,說明Class在設計中本身也是一個物件。其實
Meta Class
也是一個Class,那麼它也跟其他Class一樣有自己的isa
和super_class
指標,關係如下:
Class isa and superclass relationship from Google上圖實線是
super_class
指標,虛線是isa
指標。有幾個關鍵點需要解釋以下:- Root class (class)其實就是
NSObject
,NSObject
是沒有超類的,所以Root class(class)
的superclass指向nil。 - 每個Class都有一個
isa
指標指向唯一的Meta class - Root class(meta)的superclass指向
Root class(class)
,也就是NSObject
,形成一個迴路。 - 每個Meta class的
isa
指標都指向Root class (meta)。
- Root class (class)其實就是
super_class
表示例項物件對應的父類name
表示類名-
ivars
表示多個成員變數,它指向objc_ivar_list
結構體。在runtime.h
可以看到它的定義:struct objc_ivar_list { int ivar_count OBJC2_UNAVAILABLE; #ifdef __LP64__ int space OBJC2_UNAVAILABLE; #endif /* variable length structure */ struct objc_ivar ivar_list[1] OBJC2_UNAVAILABLE; }
objc_ivar_list
其實就是一個連結串列,儲存多個objc_ivar
,而objc_ivar
結構體儲存類的單個成員變數資訊。 -
methodLists
表示方法列表,它指向objc_method_list
結構體的二級指標,可以動態修改*methodLists
的值來新增成員方法,也是Category實現原理,同樣也解釋Category不能新增例項變數的原因。在runtime.h
可以看到它的定義:struct objc_method_list { struct objc_method_list *obsolete OBJC2_UNAVAILABLE; int method_count OBJC2_UNAVAILABLE; #ifdef __LP64__ int space OBJC2_UNAVAILABLE; #endif /* variable length structure */ struct objc_method method_list[1] OBJC2_UNAVAILABLE; }
同理,
objc_method_list
也是一個連結串列,儲存多個objc_method
,而objc_method
結構體儲存類的某個方法的資訊。 -
cache
用來快取經常訪問的方法,它指向objc_cache
結構體,後面會重點講到。 protocols
表示類遵循哪些協議
Method
Method
表示類中的某個方法,在runtime.h
檔案中找到它的定義:
/// An opaque type that represents a method in a class definition.
typedef struct objc_method *Method;
struct objc_method {
SEL method_name OBJC2_UNAVAILABLE;
char *method_types OBJC2_UNAVAILABLE;
IMP method_imp OBJC2_UNAVAILABLE;
}
其實Method
就是一個指向objc_method
結構體指標,它儲存了方法名(method_name
)、方法型別(method_types
)和方法實現(method_imp
)等資訊。而method_imp
的資料型別是IMP
,它是一個函式指標,後面會重點提及。
Ivar
Ivar
表示類中的例項變數,在runtime.h
檔案中找到它的定義:
/// An opaque type that represents an instance variable.
typedef struct objc_ivar *Ivar;
struct objc_ivar {
char *ivar_name OBJC2_UNAVAILABLE;
char *ivar_type OBJC2_UNAVAILABLE;
int ivar_offset OBJC2_UNAVAILABLE;
#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif
}
Ivar
其實就是一個指向objc_ivar
結構體指標,它包含了變數名(ivar_name
)、變數型別(ivar_type
)等資訊。
IMP
在上面講Method
時就說過,IMP
本質上就是一個函式指標,指向方法的實現,在objc.h
找到它的定義:
/// A pointer to the function of a method implementation.
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ );
#else
typedef id (*IMP)(id, SEL, ...);
#endif
當你向某個物件傳送一條資訊,可以由這個函式指標來指定方法的實現,它最終就會執行那段程式碼,這樣可以繞開訊息傳遞階段而去執行另一個方法實現。
Cache
顧名思義,Cache
主要用來快取,那它快取什麼呢?我們先在runtime.h
檔案看看它的定義:
typedef struct objc_cache *Cache OBJC2_UNAVAILABLE;
struct objc_cache {
unsigned int mask /* total = mask + 1 */ OBJC2_UNAVAILABLE;
unsigned int occupied OBJC2_UNAVAILABLE;
Method buckets[1] OBJC2_UNAVAILABLE;
};
Cache
其實就是一個儲存Method
的連結串列,主要是為了優化方法呼叫的效能。當物件receiver
呼叫方法message
時,首先根據物件receiver
的isa
指標查詢到它對應的類,然後在類的methodLists
中搜索方法,如果沒有找到,就使用super_class
指標到父類中的methodLists
查詢,一旦找到就呼叫方法。如果沒有找到,有可能訊息轉發,也可能忽略它。但這樣查詢方式效率太低,因為往往一個類大概只有20%的方法經常被呼叫,佔總呼叫次數的80%。所以使用Cache來快取經常呼叫的方法,當呼叫方法時,優先在Cache查詢,如果沒有找到,再到methodLists
查詢。
訊息傳送
前面從objc_msgSend
作為入口,逐步深入分析Runtime的資料結構,瞭解每個資料結構的作用和它們之間關係後,我們正式轉入訊息傳送這個正題。
objc_msgSend函式
在前面已經提過,當某個物件使用語法[receiver message]
來呼叫某個方法時,其實[receiver
message]
被編譯器轉化為:
id objc_msgSend ( id self, SEL op, ... );
現在讓我們看一下objc_msgSend
它具體是如何傳送訊息:
- 首先根據
receiver
物件的isa
指標獲取它對應的class
- 優先在
class
的cache
查詢message
方法,如果找不到,再到methodLists
查詢 - 如果沒有在
class
找到,再到super_class
查詢 - 一旦找到
message
這個方法,就執行它實現的IMP
。
Objc Message.gif
self與super
為了讓大家更好地理解self
和super
,借用sunnyxx部落格的ios程式設計師6級考試一道題目:下面的程式碼分別輸出什麼?
@implementation Son : Father
- (id)init
{
self = [super init];
if (self)
{
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
@end
self
表示當前這個類的物件,而super
是一個編譯器標示符,和self
指向同一個訊息接受者。在本例中,無論是[self
class]還是[super class],接受訊息者都是Son物件,但super
與self
不同的是,self呼叫class方法時,是在子類Son中查詢方法,而super呼叫class方法時,是在父類Father中查詢方法。
當呼叫[self class]
方法時,會轉化為objc_msgSend
函式,這個函式定義如下:
id objc_msgSend(id self, SEL op, ...)
這時會從當前Son類的方法列表中查詢,如果沒有,就到Father類查詢,還是沒有,最後在NSObject類查詢到。我們可以從NSObject.mm
檔案中看到-
(Class)class
的實現:
- (Class)
相關推薦
Objective-C特性:Runtime
轉載來自:http://www.jianshu.com/p/25a319aee33d
Objective-C是基於C語言加入了面向物件特性和訊息轉發機制的動態語言,這意味著它不僅需要一個編譯器,還需要Runtime系統來動態建立類和物件,進行訊息傳送和轉發。下面通過分
Objective-C 中的Runtime的詳細使用
enc ring 博客 document 每次 tps htm lec guid Runtime全面了解
一直以來,OC被大家冠以動態語言的稱謂,其實是因為OC中包含的runtime機制。Runtime 又叫運行時,是一套底層的 C 語言 API,其為 iO
C錯誤:runtime error,返回區域性變數或臨時變數的指標
在codeforce上runtime error runtime error (執行時錯誤)就是程式執行到一半,程式就崩潰了。 比如說: ①除以零 ②陣列越界:int a[3]; a[10000000]=10; ③指標越界:int * p; p=(int *)malloc(5 * si
c++ 特性: 右值引用與移動語義
c++11中引入了右值引用。
左值與右值
先區分左值與右值,這裡參考c++ 右值引用中對左值和右值區分方法:
左值和右值都是針對表示式,左值是指表示式結束後依然存在的持久物件,右值是指表示式結束時就不再存在的臨時物件。
一個區分左值和右值的簡便方法是
黑馬程式設計師Objective-C筆記:點語法
一.點語法本質
點語法的本質的本質還是方法的呼叫,不是訪問成員變數。當編譯器遇到點語法時,自動轉化成相應的語法。
二.例項應用
P.age=10;
=[P setAge:10]
set方法,設定成員變數的值
int a = P.age=[P age]
get方法,
黑馬程式設計師Objective-C筆記:類
#import //包含主標頭檔案,因為後面用到了NSobject
@interface Person : NSObject //在這裡冒號表示繼承,讓person繼承NSobject的目的是讓Person這個類具備建立物件的能力
{
// 這裡宣告屬性(預設情況初始化是0),成員變
黑馬程式設計師Objective-C筆記:封裝,繼承,多型
例子:
#import
@interface Animal : NSObject
- (void)eat;
@end
@implementation Animal
- (void)eat
{
NSLog(@"Animal---Eating something!!");
}
@end
Objective-C Runtime 執行時之五:協議與分類
Objective-C中的分類允許我們通過給一個類新增方法來擴充它(但是通過category不能新增新的例項變數),並且我們不需要訪問類中的程式碼就可以做到。
Objective-C中的協議是普遍存在的介面定義方式,即在一個類中通過@protocol定義介面,在另外
Objective-C Runtime 執行時之六:拾遺
前面幾篇基本介紹了runtime中的大部分功能,包括對類與物件、成員變數與屬性、方法與訊息、分類與協議的處理。runtime大部分的功能都是圍繞這幾點來實現的。
本章的內容並不算重點,主要針對前文中對Objective-C Runtime Reference內容遺漏
Objective-C Runtime 執行時之一:類與物件
Objective-C語言是一門動態語言,它將很多靜態語言在編譯和連結時期做的事放到了執行時來處理。這種動態語言的優勢在於:我們寫程式碼時更具靈活性,如我們可以把訊息轉發給我們想要的物件,或者隨意交換一個方法的實現等。
這種特性意味著Objective-C不僅需要一
Objective-C Runtime 執行時之二:成員變數與屬性
在前面一篇文章中,我們介紹了Runtime中與類和物件相關的內容,從這章開始,我們將討論類實現細節相關的內容,主要包括類中成員變數,屬性,方法,協議與分類的實現。
本章的主要內容將聚集在Runtime對成員變數與屬性的處理。在討論之前,我們先介紹一個重要的概念:型別
Objective-C Runtime 執行時之三:方法與訊息
前面我們討論了Runtime中對類和物件的處理,及對成員變數與屬性的處理。這一章,我們就要開始討論Runtime中最有意思的一部分:訊息處理機制。我們將詳細討論訊息的傳送及訊息的轉發。不過在討論訊息之前,我們先來了解一下與方法相關的一些內容。
基礎資料型別
SEL
Objective-C Runtime 執行時之四:Method Swizzling
理解Method Swizzling是學習runtime機制的一個很好的機會。在此不多做整理,僅翻譯由Mattt Thompson發表於nshipster的Method Swizzling一文。
Method Swizzling是改變一個selector的實際實現的
Objective-C Runtime 總結:訊息機制 篇
Objective-C語言是一門動態語言,它將很多靜態語言在編譯和連結時期做的事放到了執行時來處理。這種動態語言的優勢在於:我們寫程式碼時更具靈活性,如我們可以把訊息轉發給我們想要的物件,或者隨意交換一個方法的實現等。
與Runtime互動
Objc 從
Objective-C Runtime 執行時:成員變數(ivars)及屬性
獲取類的成員變數和屬性:
在objc_class中,所有的成員變數、屬性的資訊是放在連結串列ivars中的。ivars是一個數組,陣列中每個元素是指向Ivar(變數資訊)的指標。runtime提供了豐富的函式來操作這一欄位。大體上可以分為以下幾類:
1.成員變數操作函式
iOS學習筆記56(Runtime)-Objective-C Runtime 執行時之三:方法與訊息
前面我們討論了Runtime中對類和物件的處理,及對成員變數與屬性的處理。這一章,我們就要開始討論Runtime中最有意思的一部分:訊息處理機制。我們將詳細討論訊息的傳送及訊息的轉發。不過在討論訊息之前,我們先來了解一下與方法相關的一些內容。
基礎資料型別
SEL
objective-c runtime安全措施之二:反注入
《O'Reilly.Hacking.and.Securing.iOS.Applications>>讀書筆記
反注入:在類函式被呼叫前做完整性檢測(預防應用自定義函式或apple標準庫函式被修改或替換)
原理:呼叫dladdr()函式檢查類方法的基本資訊是否合法
第 1 條:了解 Objective-C 語言的起源
還在 特性 只知道 程序 開發 不能 核心 nbsp 原因 馬上就要發布 Swift 4 了,自己也在學習 Swift,後面 iOS 編程估計也快是 Swift 的天下了,我卻還在這抱著一本講 OC 的書在啃,怪只能怪自己之前太懶了,按定價好幾十塊錢買的書不讀完,簡直對
深入理解Objective-C:Category
fix 忽略 DDU 相關 情況 內存布局 先生 們的 ntc https://tech.meituan.com/DiveIntoCategory.html
摘要
無論一個類設計的多麽完美,在未來的需求演進中,都有可能會碰到一些無法預測的情況。那怎麽擴展已有的類呢?一般而言
Objective-C多態:動態類型識別+動態綁定+動態加載
出錯 靜態類 檢查 而不是 memberof ati 運行 strong 函數指針 http://blog.csdn.net/tskyfree/article/details/7984887
一、Objective-C多態
1.概念:相同接口,不同的實現
來自不同類可以定義