iOS-Self & Super(Runtime)

前言

關(guān)于Objective-C Runtime一篇好的文檔 : Understanding the Objective-C Runtime

譯文地址為: http://blog.cocoabit.com/blog/2014/10/06/yi-li-jieobjective-cruntime/

Objective-C Runtime源碼是開(kāi)源的,下載地址為: http://opensource.apple.com/tarballs/objc4/

習(xí)題內(nèi)容

唐巧_boy在微博上分享了他們技術(shù)討論會(huì)關(guān)于objc runtime的討論習(xí)題內(nèi)容,習(xí)題來(lái)自 sunnyxx, 他的博客地址為 http://blog.sunnyxx.com。

以下是習(xí)題內(nèi)容(圖片轉(zhuǎn)自@唐巧_boy微博):

44D1DB3F-1F56-4AA5-9C6A-F738E4FEC21E.png

自己做完這些題之后,也順便復(fù)習(xí)了一些Objective-C Runtime的知識(shí),現(xiàn)在整理一下,分享給大家。

刨根問(wèn)底

下面的代碼輸出什么?

@implementation Son : Father
- (id)init
{
    self = [super init];
    if (self)
    {
        NSLog(@"%@", NSStringFromClass([self class]));
        NSLog(@"%@", NSStringFromClass([super class]));
    }
    return self;
}
@end
2014-11-05 11:06:18.060 Test[8566:568584] NSStringFromClass([self class]) = Son
2014-11-05 11:06:18.061 Test[8566:568584] NSStringFromClass([super class]) = Son

解惑:這個(gè)題目主要是考察關(guān)于objc中對(duì) self 和 super 的理解。

self 是類(lèi)的隱藏參數(shù),指向當(dāng)前調(diào)用方法的這個(gè)類(lèi)的實(shí)例。而 super 是一個(gè) Magic Keyword, 它本質(zhì)是一個(gè)編譯器標(biāo)示符,和 self 是指向的同一個(gè)消息接受者。上面的例子不管調(diào)用[self class]還是[super class],接受消息的對(duì)象都是當(dāng)前 Son *xxx 這個(gè)對(duì)象。而不同的是,super是告訴編譯器,調(diào)用 class 這個(gè)方法時(shí),要去父類(lèi)的方法,而不是本類(lèi)里的。

當(dāng)使用 self 調(diào)用方法時(shí),會(huì)從當(dāng)前類(lèi)的方法列表中開(kāi)始找,如果沒(méi)有,就從父類(lèi)中再找;而當(dāng)使用 super 時(shí),則從父類(lèi)的方法列表中開(kāi)始找。然后調(diào)用父類(lèi)的這個(gè)方法。

真的是這樣嗎?繼續(xù)看:

使用clang重寫(xiě)命令:

$ clang -rewrite-objc test.m

發(fā)現(xiàn)上述代碼被轉(zhuǎn)化為:

NSLog((NSString *)&__NSConstantStringImpl__var_folders_gm_0jk35cwn1d3326x0061qym280000gn_T_main_a5cecc_mi_0, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"))));

NSLog((NSString *)&__NSConstantStringImpl__var_folders_gm_0jk35cwn1d3326x0061qym280000gn_T_main_a5cecc_mi_1, NSStringFromClass(((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){ (id)self, (id)class_getSuperclass(objc_getClass("Son")) }, sel_registerName("class"))));

從上面的代碼中,我們可以發(fā)現(xiàn)在調(diào)用 [self class] 時(shí),會(huì)轉(zhuǎn)化成 objc_msgSend函數(shù)??聪潞瘮?shù)定義:

id objc_msgSend(id self, SEL op, ...)

我們把 self 做為第一個(gè)參數(shù)傳遞進(jìn)去。

而在調(diào)用 [super class]時(shí),會(huì)轉(zhuǎn)化成 objc_msgSendSuper函數(shù)??聪潞瘮?shù)定義:

id objc_msgSendSuper(struct objc_super *super, SEL op, ...)

第一個(gè)參數(shù)是 objc_super 這樣一個(gè)結(jié)構(gòu)體,其定義如下:

struct objc_super {
   __unsafe_unretained id receiver;
   __unsafe_unretained Class super_class;
};

結(jié)構(gòu)體有兩個(gè)成員,第一個(gè)成員是 receiver, 類(lèi)似于上面的 objc_msgSend函數(shù)第一個(gè)參數(shù)self 。第二個(gè)成員是記錄當(dāng)前類(lèi)的父類(lèi)是什么。

所以,當(dāng)調(diào)用 [self class] 時(shí),實(shí)際先調(diào)用的是 objc_msgSend函數(shù),第一個(gè)參數(shù)是 Son當(dāng)前的這個(gè)實(shí)例,然后在 Son 這個(gè)類(lèi)里面去找 - (Class)class這個(gè)方法,沒(méi)有,去父類(lèi) Father里找,也沒(méi)有,最后在 NSObject類(lèi)中發(fā)現(xiàn)這個(gè)方法。而 - (Class)class的實(shí)現(xiàn)就是返回self的類(lèi)別,故上述輸出結(jié)果為 Son。

objc Runtime開(kāi)源代碼對(duì)- (Class)class方法的實(shí)現(xiàn):

- (Class)class {
    return object_getClass(self);
}

而當(dāng)調(diào)用 [super class]時(shí),會(huì)轉(zhuǎn)換成objc_msgSendSuper函數(shù)。第一步先構(gòu)造 objc_super 結(jié)構(gòu)體,結(jié)構(gòu)體第一個(gè)成員就是 self 。第二個(gè)成員是 (id)class_getSuperclass(objc_getClass(“Son”)) , 實(shí)際該函數(shù)輸出結(jié)果為 Father。第二步是去 Father這個(gè)類(lèi)里去找- (Class)class,沒(méi)有,然后去NSObject類(lèi)去找,找到了。最后內(nèi)部是使用 objc_msgSend(objc_super->receiver, @selector(class))去調(diào)用,此時(shí)已經(jīng)和[self class]調(diào)用相同了,故上述輸出結(jié)果仍然返回 Son。

搬運(yùn)自http://chun.tips/2014/11/05/objc-runtime-1/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容