創(chuàng)建時間: 2016-12-12
狀態(tài):發(fā)布
簡介
Runtime又叫運行時,是一套底層C語言的API,是iOS核心,平時編寫的OC代碼,底層都是基于它實現(xiàn)的。
[receiver message];
// 底層會被編譯器轉(zhuǎn)換為這樣再運行
objc_mesgSend(receiver, selector)
// 如果帶有參數(shù)
[receiver message:(id)arg...];
// 底層會被編譯器轉(zhuǎn)換為這樣再運行
objc_mesgSend(receiver, selector, arg1, arg2,...)
可能光看代碼看不出來他的價值,但是需Objective-C是一門動態(tài)語言,會將一些工作放在代碼運行時才處理,而不是在編譯的時候。即:很多類的成員變量在編譯的時候系統(tǒng)是不知道的,只有在運行時,加入內(nèi)存中才能知道。在運行時,我們所編寫的代碼會轉(zhuǎn)換成完整的能夠執(zhí)行的底層代碼再運行
因此,單單擁有編譯器是遠遠不夠的,還需要一個運行時系統(tǒng)Runtime system來處理編譯后的代碼。
Runtime基本是用C和匯編寫的,蘋果和GNU各自維護一個開源的Runtime版本,這兩個版本也在努力保持一致。
Runtime的作用
Objc在三種層面上與Runtime系統(tǒng)進行交互:
- 通過
Objective-C源碼 - 通過
Foundation框架的NSObject類定義的方法 - 通過對Runtime庫函數(shù)的直接調(diào)用
Objective-C源代碼
多數(shù)情況下,Runtime系統(tǒng)會自動在幕后幫助我們做完從OC語言到C執(zhí)行語言的轉(zhuǎn)換工作,在運行時確定數(shù)據(jù)結(jié)構(gòu)和函數(shù)
通過Foundation框架的NSObject類定義的方法和Runtime交互
Cocoa程序中大部分的類都是NSObject類的子類,繼承了它的行為。(NSProxy類例外,它是個抽象超類)
一些情況下,NSObject類僅僅定義了完成某件事情的模板,并沒有提供所需要的代碼。如- description方法,該方法返回類內(nèi)容的字符串標識,一般在模型中重寫這個方法,輸出模型內(nèi)容并調(diào)試程序。NSObject類并不知道子類的內(nèi)容,如模型類,所以它只是返回類的名字和對象的地址,NSObject的子類可以重新實現(xiàn)。
還有一些NSObject的方法可以從Runtime系統(tǒng)中獲取信息,運行對象進行自我檢查。
如:
-
- class返回對象的類 -
- isKindOfClass:和- isMemberOfClass:方法檢查對象是否存在于指定的類的繼承體系中(子類或父類 / 當前類的成員變量) -
- respondsToSelector:檢查對象能否響應指定的消息(一般是在代理模式中判斷代理是否實現(xiàn)類代理方法) -
- conformsToProtocol:檢查對象是否實現(xiàn)了指定協(xié)議類的方法 -
- methodForSelector:返回指定方法實現(xiàn)的地址
通過對Runtime庫函數(shù)直接調(diào)用
Runtime系統(tǒng)是具有公用接口的動態(tài)共享庫。頭文件存放在/usr/include/objc目錄下,這以為這我們使用時只需要引入objc/Runtime.h頭文件即可
許多函數(shù)可以讓你使用純C代碼來實現(xiàn)Objc中同樣的功能。除非是寫一些Objc與其他語言的橋接或是底層的debug工作,在寫Objc代碼時一般不會用到這些C語言函數(shù)。
一些Runtime術(shù)語的數(shù)據(jù)結(jié)構(gòu)
常見的Runtime術(shù)語
- SEL
- id
- Class
- Method
- Ivar
- IMP
- Cache
- Property
SEL
是selector在Objec中表示(Swift中是Selector類)selector是方法的選擇器,作用和名字一樣,日常生活中,我們通過人名來辨別不同的人,注意Objc在相同的類中不會有兩個命名相同的方法,也就是說,會通過方法名來辨別不同的方法。selector對方法名進行包裝,以便找到對應的方法實現(xiàn)。其結(jié)構(gòu)是:
typedef struct objc_selector *SEL; // 結(jié)構(gòu)體
可以看出他是個映射到方法的C字符串,可以通過Objc編譯器命令@selector()或Runtime系統(tǒng)的sel_registerName函數(shù)來獲取一個SEL類型的方法選擇器
注意
不同的類中相同名字的方法所對應的selector是相同的,由于變量的類型不同,所以不會導致他們調(diào)用方法實現(xiàn)混亂
id
id 是一個參數(shù)類型,他是指向某個類的示例的指針,定義如下
typedef struct objc_object *id;
struct objc_objct{ Class isa; };
以上定義,看到objc_object結(jié)構(gòu)體包含一個isa指針,根據(jù)isa指針就可以找到對象所屬的類
注意:
isa指針在代碼運行時并不總指向示例對象所屬的類型,所以不能依靠它來確定類型,要想確定類型還是需要用對象的-class方法
PS: KVO的時間機制就是將被觀察對象的isa指針指向一個中間類而不是真實類型:KVO
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可以看到,一個運行時類中關聯(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不能添加屬性的原因。美團技術(shù)團隊的文章:深入理解Objective-C
objc_ivar_list 結(jié)構(gòu)體用來存儲成員變量的列表,而 objc_ivar 則是存儲了單個成員變量的信息;同理,objc_method_list 結(jié)構(gòu)體存儲著方法數(shù)組的列表,而單個方法的信息則由 objc_method 結(jié)構(gòu)體存儲。
值得注意的時,objc_class 中也有一個 isa 指針,這說明 Objc 類本身也是一個對象。為了處理類和對象的關系,Runtime 庫創(chuàng)建了一種叫做 Meta Class(元類) 的東西,類對象所屬的類就叫做元類。Meta Class 表述了類對象本身所具備的元數(shù)據(jù)。
我們所熟悉的類方法,就源自于 Meta Class。我們可以理解為類方法就是類對象的實例方法。每個類僅有一個類對象,而每個類對象僅有一個與之相關的元類。
當你發(fā)出一個類似 NSObject alloc 的消息時,實際上,這個消息被發(fā)送給了一個類對象(Class Object),這個類對象必須是一個元類的實例,而這個元類同時也是一個根元類(Root Meta Class)的實例。所有元類的 isa 指針最終都指向根元類。
所以當 [NSObject alloc] 這條消息發(fā)送給類對象的時候,運行時代碼 objc_msgSend() 會去它元類中查找能夠響應消息的方法實現(xiàn),如果找到了,就會對這個類對象執(zhí)行方法調(diào)用。

上圖實現(xiàn)是 super_class 指針,虛線時 isa 指針。而根元類的父類是 NSObject,isa指向了自己。而 NSObject 沒有父類。
最后 objc_class 中還有一個 objc_cache ,緩存,它的作用很重要
Category擴展
Objective-C的這個語言特性對于純動態(tài)語言來說可能不算什么,比如javascript,你可以隨時為一個“類”或者對象添加任意方法和實例變量。但是對于不是那么“動態(tài)”的語言而言,這確實是一個了不起的特性。
objc_ivar_list 結(jié)構(gòu)體用來存儲成員變量的列表,而 objc_ivar 則是存儲了單個成員變量的信息;同理,objc_method_list 結(jié)構(gòu)體存儲著方法數(shù)組的列表,而單個方法的信息則由 objc_method 結(jié)構(gòu)體存儲。
值得注意的時,objc_class 中也有一個 isa 指針,這說明 Objc 類本身也是一個對象。為了處理類和對象的關系,Runtime 庫創(chuàng)建了一種叫做 Meta Class(元類) 的東西,類對象所屬的類就叫做元類。Meta Class 表述了類對象本身所具備的元數(shù)據(jù)。
我們所熟悉的類方法,就源自于 Meta Class。我們可以理解為類方法就是類對象的實例方法。每個類僅有一個類對象,而每個類對象僅有一個與之相關的元類。
當你發(fā)出一個類似 NSObject alloc 的消息時,實際上,這個消息被發(fā)送給了一個類對象(Class Object),這個類對象必須是一個元類的實例,而這個元類同時也是一個根元類(Root Meta Class)的實例。所有元類的 isa 指針最終都指向根元類。
所以當 [NSObject alloc] 這條消息發(fā)送給類對象的時候,運行時代碼 objc_msgSend() 會去它元類中查找能夠響應消息的方法實現(xiàn),如果找到了,就會對這個類對象執(zhí)行方法調(diào)用。
Category和Extension
extension看起來很像一個匿名的category,但是extension和有名字的category幾乎完全是兩個東西。 extension在編譯期決議,它就是類的一部分,在編譯期和頭文件里的@interface以及實現(xiàn)文件里的@implement一起形成一個完整的類,它伴隨類的產(chǎn)生而產(chǎn)生,亦隨之一起消亡。extension一般用來隱藏類的私有信息,你必須有一個類的源碼才能為一個類添加extension,所以你無法為系統(tǒng)的類比如NSString添加extension。
但是category則完全不一樣,它是在運行期決議的。
就category和extension的區(qū)別來看,我們可以推導出一個明顯的事實,extension可以添加實例變量,而category是無法添加實例變量的(因為在運行期,對象的內(nèi)存布局已經(jīng)確定,如果添加實例變量就會破壞類的內(nèi)部布局,這對編譯型語言來說是災難性的)。
參考: