runtime class isa
所有繼承自 NSObject 的類實(shí)例化后的對(duì)象都會(huì)包含一個(gè)類型為 isa_t 的結(jié)構(gòu)體界轩。不只是實(shí)例會(huì)包含一個(gè) isa 結(jié)構(gòu)體彤蔽,所有的類也有這么一個(gè) isa
struct objc_object {
private:
isa_t isa;
...
}
最關(guān)鍵的就是isa_t這個(gè)類型和一系列的構(gòu)造函數(shù),點(diǎn)擊查看isa_t發(fā)現(xiàn)這是一個(gè)聯(lián)合體:
union isa_t
{
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
}
可以看到這個(gè)聯(lián)合體里面有個(gè)Class類型的屬性cls,看起來里面應(yīng)該是關(guān)于這個(gè)對(duì)象的類的相關(guān)信息,那我們?cè)倏纯碈lass包含了哪些內(nèi)容棉圈。
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // 方法緩存
class_data_bits_t bits; //bits 里面存放這當(dāng)前類屬性涩堤,方法眷蜓、協(xié)議等信息
...
}
由此可見Class是一個(gè)objc_class類型的結(jié)構(gòu)體,而objc_class繼承自objc_object胎围,說明類也是一個(gè)對(duì)象吁系,只是比普通的對(duì)象多了一些屬性,比如superclass等白魂。
既然類也是一個(gè)objc_object汽纤,那就是說類也有一個(gè)isa指針,那類的isa指針指向哪里呢福荸?
在class之上還有叫做元類(meta class)的存在蕴坪,而class的isa指針就是指向?qū)?yīng)的meta class,
meta class的isa指向root meta class(絕大部分情況下是NSObject),root meta class的isa指針指向自己敬锐。class中存儲(chǔ)的是描述對(duì)象的相關(guān)信息背传,那么相應(yīng)的meta class中存放的就是描述class相關(guān)信息。說的更直白一點(diǎn)台夺,在我們寫代碼時(shí)径玖,通過對(duì)象來調(diào)用的方法(實(shí)例方法)都是存儲(chǔ)在class中的,通過類名來調(diào)用的方法(類方法)都是存儲(chǔ)在meta class中的
當(dāng)實(shí)例方法被調(diào)用時(shí)颤介,它要通過自己持有的 isa 來查找對(duì)應(yīng)的類梳星,然后在這里的 class_data_bits_t 結(jié)構(gòu)體中查找對(duì)應(yīng)方法的實(shí)現(xiàn)。同時(shí)滚朵,每一個(gè) objc_class 也有一個(gè)指向自己的父類的指針 super_class 用來查找繼承的方法冤灾。
實(shí)驗(yàn):
TestObject *testObj = [TestObject new];
NSLog(@"%d", [testObj class] == [TestObject class]);
這個(gè)log會(huì)輸出1(為true)
不好理解,看下具體源碼辕近,如下
+ (Class)class {
return self;
}
- (Class)class {
return object_getClass(self);
}
object_getClass方法最終返回的是isa瞳购。所以TestObject調(diào)用class方法,返回的是自身亏推;testObj調(diào)用class方法学赛,返回的是isa指向的類,也是TestObject吞杭。
舉一反三:
[self class] 與 [super class]
@implementation Son : Father
- (id)init
{
self = [super init];
if (self) {
NSLog(@"%@", NSStringFromClass([self class]));
NSLog(@"%@", NSStringFromClass([super class]));
}
return self;
}
@end
眼熟盏浇,在哪里見過你,是的芽狗,很多有筆試經(jīng)歷小伙伴會(huì)碰到這題绢掰,看似很簡(jiǎn)單,但是被問為什么的時(shí)候?這么簡(jiǎn)單滴劲,居然問為什么攻晒,當(dāng)時(shí)還是太嫩了,還是太年輕了班挖。
詳解:
NSStringFromClass([self class]) = Son
NSStringFromClass([super class]) = Son
self 是類的隱藏參數(shù)鲁捏,指向當(dāng)前調(diào)用方法的這個(gè)類的實(shí)例;
super 本質(zhì)是一個(gè)編譯器標(biāo)示符萧芙,和 self 是指向的同一個(gè)消息接受者给梅。不同點(diǎn)在于:super 會(huì)告訴編譯器,當(dāng)調(diào)用方法時(shí)双揪,去調(diào)用父類的方法动羽,而不是本類中的方法。
當(dāng)使用 self 調(diào)用方法時(shí)渔期,會(huì)從當(dāng)前類的方法列表中開始找运吓,如果沒有,就從父類中再找疯趟;而當(dāng)使用 super 時(shí)拘哨,則從父類的方法列表中開始找。然后調(diào)用父類的這個(gè)方法迅办。
在調(diào)用[super class]的時(shí)候宅静,runtime會(huì)去調(diào)用objc_msgSendSuper方法,而不是objc_msgSend站欺;
OBJC_EXPORT void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )
/// Specifies the superclass of an instance.
struct objc_super {
/// Specifies an instance of a class.
__unsafe_unretained id receiver;
/// Specifies the particular superclass of the instance to message.
#if !defined(__cplusplus) && !__OBJC2__
/* For compatibility with old objc-runtime.h header */
__unsafe_unretained Class class;
#else
__unsafe_unretained Class super_class;
#endif
/* super_class is the first class to search */
};
在objc_msgSendSuper方法中姨夹,第一個(gè)參數(shù)是一個(gè)objc_super的結(jié)構(gòu)體,這個(gè)結(jié)構(gòu)體里面有兩個(gè)變量矾策,一個(gè)是接收消息的receiver磷账,一個(gè)是當(dāng)前類的父類super_class。
objc_msgSendSuper的工作原理應(yīng)該是這樣的:
從objc_super結(jié)構(gòu)體指向的superClass父類的方法列表開始查找selector贾虽,找到后以objc->receiver去調(diào)用父類的這個(gè)selector逃糟。注意,最后的調(diào)用者是objc->receiver蓬豁,而不是super_class绰咽!
那么objc_msgSendSuper最后就轉(zhuǎn)變成:
// 注意這里是從父類開始msgSend,而不是從本類開始
objc_msgSend(objc_super->receiver, @selector(class))
/// Specifies an instance of a class. 這是類的一個(gè)實(shí)例
__unsafe_unretained id receiver;
// 由于是實(shí)例調(diào)用地粪,所以是減號(hào)方法
- (Class)class {
return object_getClass(self);
}
這里 其實(shí)就是 [self class] == [Son class] ,又回到之前講過的isa知識(shí)了
由于找到了父類NSObject里面的class方法的IMP(一個(gè)函數(shù)指針,保存了方法的地址)取募,又因?yàn)閭魅氲娜雲(yún)bjc_super->receiver = self。self就是son蟆技,調(diào)用class玩敏,所以父類的方法class執(zhí)行IMP之后斗忌,輸出還是son,最后輸出兩個(gè)都一樣旺聚,都是輸出son织阳。