Runtime最全總結(jié)
本系列詳細(xì)講解Runtime知識(shí)點(diǎn),由于運(yùn)行時(shí)的內(nèi)容較多,所以將內(nèi)容拆分成以下幾個(gè)方面,可以自行選擇想要查看的部分
- OC運(yùn)行時(shí)機(jī)制Runtime(一):從isa指針開始初步結(jié)識(shí)Runtime
- OC運(yùn)行時(shí)機(jī)制Runtime(二):探索Runtime的消息轉(zhuǎn)發(fā)機(jī)制和分類Category
- OC運(yùn)行時(shí)機(jī)制Runtime(三):關(guān)聯(lián)對(duì)象Associated Object和分類Category
- OC運(yùn)行時(shí)機(jī)制Runtime(四):嘗試使用黑魔法 Method Swizzling
本文主要分析Runtime的基本特性,探究OC面向?qū)ο蟮幕緦?shí)現(xiàn)务漩。
什么是運(yùn)行時(shí)?
眾所周知Objective-C是一門動(dòng)態(tài)語言它褪,動(dòng)態(tài)語言和靜態(tài)語言的區(qū)別是動(dòng)態(tài)語言可以在運(yùn)行期決定饵骨,一些類或?qū)ο罂梢栽谶\(yùn)行期暴露出來,而不需要在編譯時(shí)決定是否有這個(gè)類或?qū)ο竺4颍呙黠@區(qū)別是居触,動(dòng)態(tài)語言取決于運(yùn)行環(huán)境,靜態(tài)語言取決于編譯器老赤,例如使用多態(tài)時(shí)轮洋,靜態(tài)語言在運(yùn)行時(shí)要根據(jù)虛方法表查出應(yīng)該是哪個(gè)函數(shù)實(shí)現(xiàn),而動(dòng)態(tài)語言無論是否多態(tài)下都會(huì)在運(yùn)行時(shí)查找要執(zhí)行的方法抬旺,在這里Runtime是實(shí)現(xiàn)這門動(dòng)態(tài)語言的基礎(chǔ)弊予,從把面向?qū)ο蟮念惥幾g為面向過程的結(jié)構(gòu)體少不了它的作用,也許我們平時(shí)工作中不是很直觀的經(jīng)常使用它开财,但是可以說如果沒有Runtime就沒有Objective-C這門語言的成功汉柒。
isa指針?责鳍?這啥玩意D牍印!
首先isa指針的全稱历葛,is a kind of 指針正塌,顧名思義我們可以先理解為指向它所在類型的指針,如果一個(gè)類創(chuàng)建了一個(gè)實(shí)例,那么可以通過這個(gè)指針指向找到所在的類乓诽,下面打開objc.h文件
/// An opaque type that represents an Objective-C class.
typedef struct objc_class *Class;
/// Represents an instance of a class.
struct objc_object {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
};
看的出每個(gè)objc_object對(duì)象都有一個(gè)指向Class類型的isa指針帜羊,再打開runtime.h文件
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
/* Use `Class` instead of `struct objc_class *` */
這里有Class類型的isa指針還有super_class,也可以看出isa指針并不是指向父類指針鸠天,這個(gè)結(jié)構(gòu)體里面的內(nèi)容很直觀逮壁,isa指針、指向父類指針粮宛、名稱、版本卖宠、信息巍杈、變量大小、變量列表扛伍、方法列表等等筷畦,每個(gè)objc_object可以通過isa指針找到它的類,并找到想要實(shí)現(xiàn)的方法或遵循的協(xié)議刺洒,由于objc_class也有isa指針鳖宾,所以objc_class也是一個(gè)對(duì)象,稱為“類對(duì)象”逆航,它的isa指針指向他的元類(Meta-Class)鼎文,這樣一來就很清晰了,每個(gè)對(duì)象通過isa指針向類中查找信息因俐,類對(duì)象通過isa指針向元類查找信息拇惋,每個(gè)實(shí)例對(duì)象或類對(duì)象根據(jù)super_class指針都可以找到它們的父類,至此整個(gè)繼承傳遞結(jié)構(gòu)出來了抹剩,引用大神的圖
到此為止有些朋友可能對(duì)isa和superclass這兩個(gè)指針的含義理解的不是很清晰,那么下面用一個(gè)常見的案例來分析一下
假設(shè)有Father和Son兩個(gè)類澳眷,Son繼承自Father類胡嘿,在Son.m文件中寫下以下代碼
#import "Son.h"
@implementation Son
- (instancetype)init {
if (self = [super init]) {
NSLog(@"%@", [self class]);
NSLog(@"%@", [super class]);
NSLog(@"%@", self.superclass);
}
return self;
}
@end
初始化Son類的實(shí)例對(duì)象,觀察一下打印結(jié)果
2019-02-25 17:07:35.386429+0800 Demo[21294:1762905] Son
2019-02-25 17:07:35.386637+0800 Demo[21294:1762905] Son
2019-02-25 17:07:35.386670+0800 Demo[21294:1762905] Father
可以看出[super class]
的打印結(jié)果并不是Father钳踊,而self.superclass
的輸出結(jié)果是Father衷敌。這里用Runtime的角度分析一下出現(xiàn)這個(gè)結(jié)果的原因。
首先super關(guān)鍵字并不是指self的父類對(duì)象箍土,它的作用是訪問從當(dāng)前對(duì)象或類從父類繼承過來的方法逢享,其實(shí)等同于isa指針從當(dāng)前類的方法列表中找不到class方法,需要向元類中查找吴藻,如果找不到則繼續(xù)向父類依次查找直到NSObject瞒爬,這里無論是self還是super最后都是在基類的方法中找到class這個(gè)方法,而方法的調(diào)用者(OC中指消息的接受者)都是self,所以都是Son類
superclass指針直接指向當(dāng)前類的父類侧但,所以輸出是Father矢空,這兩個(gè)方法有本質(zhì)上的區(qū)別
使用clang將Son.m文件編譯成cpp文件clang -rewrite-objc Son.m
在最下面找到這段c++代碼
NSLog((NSString *)&__NSConstantStringImpl__var_folders__w_bfstkhhs0c99d31tbzcqycpm0000gp_T_Son_127820_mi_0, ((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class")));
NSLog((NSString *)&__NSConstantStringImpl__var_folders__w_bfstkhhs0c99d31tbzcqycpm0000gp_T_Son_127820_mi_1, ((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("class")));
NSLog((NSString *)&__NSConstantStringImpl__var_folders__w_bfstkhhs0c99d31tbzcqycpm0000gp_T_Son_127820_mi_2, ((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("superclass")));
這段代碼太長了,我們只保留帶類型的關(guān)鍵部分將它簡(jiǎn)化一下
((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"))
((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Son"))}, sel_registerName("class"))
((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("superclass"))
這里有一個(gè)類型__rw_objc_super
禀横,只要使用super關(guān)鍵字調(diào)用方法屁药,都會(huì)構(gòu)建這個(gè)結(jié)構(gòu)體,根據(jù)isa指針向上查找方法柏锄,將所有類型全部去掉得到最簡(jiǎn)化的代碼
(self, sel_registerName("class"))
({self,class_getSuperclass(objc_getClass("Son"))},sel_registerName("class"))
(self, sel_registerName("superclass"))
最簡(jiǎn)化代碼看得出酿箭,前兩個(gè)雖然代碼不同,但是receiver參數(shù)都是self趾娃,最后調(diào)用的方法都是class缭嫡,只是查找的來源不是同一個(gè)對(duì)象,而最后一個(gè)self接收的方法是superclass查找的是父類抬闷,這樣也就印證了上面的理論妇蛀。
這里如果消息傳遞內(nèi)容不是很清晰,請(qǐng)移步到下一篇文章OC運(yùn)行時(shí)機(jī)制Runtime(二):探索Runtime的消息轉(zhuǎn)發(fā)機(jī)制
接下來再用一個(gè)案例來確定確實(shí)有Meta-Class這個(gè)概念笤成。
NSLog(@"%@", object_getClass(self));
NSLog(@"%@", object_getClass([self class]));
NSLog(@"%d", class_isMetaClass(object_getClass(self)));
NSLog(@"%d", class_isMetaClass(object_getClass([self class])));
這兩個(gè)函數(shù)很容易看懂评架,分別是獲取當(dāng)前的類對(duì)象,以及判斷當(dāng)前類是否為元類炕泳,輸出結(jié)果如下
2019-02-28 17:05:26.138644+0800 Demo[3114:1623664] Son
2019-02-28 17:05:26.138948+0800 Demo[3114:1623664] Son
2019-02-28 17:05:26.138971+0800 Demo[3114:1623664] 0
2019-02-28 17:05:26.138987+0800 Demo[3114:1623664] 1
這下很清晰了纵诞,雖然同樣結(jié)果都是Son類,但是其中一個(gè)是類對(duì)象培遵,另一個(gè)是元類挣磨,所以可以判斷,一個(gè)實(shí)例對(duì)象的isa指針指向它的類對(duì)象荤懂,而類對(duì)象的isa指針則指向它的元類茁裙。
總結(jié)
super關(guān)鍵字不代表父類對(duì)象,而是從isa指針指向的對(duì)象节仿,而消息的接受者還是self本身晤锥。每個(gè)對(duì)象都有一個(gè)isa指針,實(shí)例對(duì)象的指針指向它的類廊宪,類對(duì)象的指針指向元類矾瘾,所有的Class對(duì)象構(gòu)成了類的繼承體系,所以箭启,每個(gè)對(duì)象所要執(zhí)行的方法都要由isa指針向上查找壕翩,實(shí)例對(duì)象的方法存在于類中,類方法存在于元類中傅寡,這樣就完整的將OC的類映射到了C的結(jié)構(gòu)體上了放妈。
后續(xù)
到這里只分析了Runtime的基本關(guān)系和指針傳遞北救,后文會(huì)繼續(xù)分析消息機(jī)制和關(guān)聯(lián)對(duì)象等知識(shí)點(diǎn),感興趣的朋友們可以移步下一篇文章 OC運(yùn)行時(shí)機(jī)制Runtime(二):探索Runtime的消息轉(zhuǎn)發(fā)機(jī)制芜抒,如果覺得本文對(duì)您有些作用珍策,請(qǐng)?jiān)谙路近c(diǎn)個(gè)贊再走哈~