Runtime 又叫運行時,是一套底層的 C 語言 API,其為 iOS 內(nèi)部的核心之一,我們平時編寫的 OC 代碼,底層都是基于它來實現(xiàn)的。比如:
[receiver message];
底層運行時會被編譯器轉(zhuǎn)化為:
objc_msgSend(receiver, selector)
如果其還有參數(shù)比如:
[receiver message:(id)arg...];
底層運行時會被編譯器轉(zhuǎn)化為:
objc_msgSend(receiver, selector, arg1, arg2, ...)
Runtime 的作用
Objc 在三種層面上與 Runtime 系統(tǒng)進行交互:
1、 通過 Objective-C 源代碼(編譯器會將 OC 代碼轉(zhuǎn)換成運行時代碼,在運行時確定數(shù)據(jù)結(jié)構(gòu)和函數(shù)。)
2、 通過 Foundation 框架的 NSObject 類定義的方法
Cocoa 程序中絕大部分類都是 NSObject 類的子類,所以都繼承了 NSObject 的行為。(NSProxy 類時個例外,它是個抽象超類)
一些情況下,NSObject 類僅僅定義了完成某件事情的模板,并沒有提供所需要的代碼。例如-description方法,該方法返回類內(nèi)容的字符串表示,該方法主要用來調(diào)試程序。NSObject 類并不知道子類的內(nèi)容,所以它只是返回類的名字和對象的地址,NSObject 的子類可以重新實現(xiàn)。
還有一些 NSObject 的方法可以從 Runtime 系統(tǒng)中獲取信息,允許對象進行自我檢查。例如:
- class方法返回對象的類;
- isKindOfClass: 和 -isMemberOfClass: 方法檢查對象是否存在于指定的類的繼承體系中(是否是其子類或者父類或者當(dāng)前類的成員變量);
- respondsToSelector: 檢查對象能否響應(yīng)指定的消息;
- conformsToProtocol:檢查對象是否實現(xiàn)了指定協(xié)議類的方法;
- methodForSelector: 返回指定方法實現(xiàn)的地址。
3、通過對 Runtime 庫函數(shù)的直接調(diào)用
Runtime 系統(tǒng)是具有公共接口的動態(tài)共享庫。頭文件存放于/usr/include/objc目錄下,這意味著我們使用時只需要引入objc/Runtime.h頭文件即可。
id
id 是一個參數(shù)類型,它是指向某個類的實例的指針。定義如下:
typedef struct objc_object *id;
struct objc_object { Class isa; }
以上定義,看到 objc_object結(jié)構(gòu)體包含一個isa 指針,根據(jù) isa 指針就可以找到對象所屬的類。
Class
typedef struct objc_class *Class;
Class其實是指向objc_class結(jié)構(gòu)體的指針。objc_class的數(shù)據(jù)結(jié)構(gòu)如下:
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;
從 objc_class 可以看到,一個運行時類中關(guān)聯(lián)了它的父類指針、類名、成員變量、方法、緩存以及附屬的協(xié)議。
其中objc_ivar_list和 objc_method_list分別是成員變量列表和方法列表:
// 成員變量列表
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;
} OBJC2_UNAVAILABLE;
// 方法列表
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;
}
由此可見,我們可以動態(tài)修改 *methodList 的值來添加成員方法,這也是 Category 實現(xiàn)的原理,同樣解釋了 Category 不能添加屬性的原因。
objc_ivar_list 結(jié)構(gòu)體用來存儲成員變量的列表,而 objc_ivar 則是存儲了單個成員變量的信息;同理,objc_method_list 結(jié)構(gòu)體存儲著方法數(shù)組的列表,而單個方法的信息則由 objc_method 結(jié)構(gòu)體存儲。
值得注意的是,objc_class中也有一個isa指針,這說明Objc類本身也是一個對象。為了處理類和對象的關(guān)系,Runtime庫創(chuàng)建了一個Meta Class(元類)的東西,類對象所屬的類就叫元類。Meta Class 表述了類對象本身所具備的元數(shù)據(jù)。
我們所熟悉的類方法,就源自于 Meta Class。我們可以理解為類方法就是類對象的實例方法。每個類僅有一個類對象,而每個類對象僅有一個與之相關(guān)的元類。