05-探索方法的歸屬和isa的走向

[toc]

探索1: 方法的歸屬

通過(guò)上一節(jié), 我們學(xué)習(xí)到了

  1. 通過(guò)lldb和內(nèi)存地址, 從類以及元類里查找我們聲明的(class_ro_t *)(成員變量列表), property_array_t(屬性列表) ,method_array_t(方法列表), protocol_array_t(protocol_列表)
  2. isa和類繼承關(guān)系走位
    那這一節(jié)我們主要來(lái)通過(guò)上節(jié)學(xué)到的知識(shí)點(diǎn)

1: 探索class_getInstanceMethod

我們先來(lái)看代碼, 看完代碼問(wèn)題也就出現(xiàn)了, 他們分別會(huì)輸出什么呢?
接下來(lái)請(qǐng)大家?guī)е康母乙黄鹛剿魉伎及?


初始化類對(duì)象方式:
1: Class pClass = LLMethodClass.class;
or
2: Class pClass = object_getClass(person);
Class metaClass = objc_getMetaClass(className);
// - (void)sayHello;
// + (void)sayHappy;
Method method1 = class_getInstanceMethod(pClass, @selector(sayHello));
Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));

Method method3 = class_getInstanceMethod(pClass, @selector(sayHappy));
Method method4 = class_getInstanceMethod(metaClass, @selector(sayHappy));

  1. class_getInstanceMethod作用: 尋找實(shí)例方法
圖1: class_getInstanceMethod方法實(shí)現(xiàn)-c749
  1. 我們?cè)趏bjc源碼中查看它的源碼:


    源碼-c545
  2. 這里我們分別對(duì)lookUpImpOrForward_class_getMethod進(jìn)行進(jìn)一步探索, 發(fā)現(xiàn)大部分都看不懂, 但有一個(gè)方法讓我看到了一絲絲熟悉的地方, 請(qǐng)看下圖:

    圖2: getMethodNoSuper_nolock方法實(shí)現(xiàn)-c749

圖中標(biāo)識(shí)的地方, 讓我想到了 objc_class結(jié)構(gòu)體, 以及我們上一節(jié)找到bits(平移32位內(nèi)存)取到它里面存儲(chǔ)的成員變量(ro), 屬性(properties), 方法(methods)等等, 當(dāng)然圖只截了一小部分, 感興趣的可以自己去看下:

圖3: objc_class結(jié)構(gòu)體回顧-c346

  1. 那么聯(lián)想到這里, 我們就能大概猜到, 當(dāng)我們調(diào)用class_getInstanceMethod方法時(shí), 它在源碼里會(huì)根據(jù)傳入的cls, 來(lái)查找它的結(jié)構(gòu)體里有沒(méi)有我們要獲取的方法.
  2. 那我們?cè)诨剡^(guò)頭來(lái)看class_getInstanceMethod(pClass, @selector(sayHello));這段代碼, 就是要讓我們根據(jù)cls類的bits里, 來(lái)查找是否有sel這個(gè)實(shí)例方法, 驗(yàn)證(平移cls內(nèi)存地址獲取bits, 打印methods)后, 發(fā)現(xiàn)是有這個(gè)方法的.
  3. Method method2 = class_getInstanceMethod(metaClass, @selector(sayHello));步驟5相同的驗(yàn)證方法, 發(fā)現(xiàn)元類里是沒(méi)有sayHello方法的, 但是我們找到了sayHappy的方法, 說(shuō)到這里要拓展一個(gè)知識(shí)點(diǎn)在源碼的世界里, 只有實(shí)例方法, 是沒(méi)有類方法這個(gè)說(shuō)法的(OC層面才會(huì)有這些說(shuō)法), 因?yàn)槊總€(gè)類每個(gè)元類, 他們都是對(duì)象, 他們的方法都存儲(chǔ)在他們的父級(jí)里.
  4. 接下來(lái)繼續(xù)從他的超類里查找, 也是沒(méi)有的.
  5. 接下來(lái), 我們也就知道m(xù)ethod4和method5的結(jié)果, 在類里找類方法肯定沒(méi)有, 在元類里找類方法, 那一找一個(gè)準(zhǔn).
  6. 最后我們來(lái)輸出驗(yàn)證下:


    最終輸出結(jié)果-c749

2: 探索class_getClassMethod


初始化類對(duì)象方式:
1: Class pClass = LLMethodClass.class;
or
2: Class pClass = object_getClass(person);
Class metaClass = objc_getMetaClass(className);
// - (void)sayHello;
// + (void)sayHappy;
Method method1 = class_getClassMethod(pClass, @selector(sayHello));
Method method2 = class_getClassMethod(metaClass, @selector(sayHello));

Method method3 = class_getClassMethod(pClass, @selector(sayHappy));
Method method4 = class_getClassMethod(metaClass, @selector(sayHappy));

  1. 蘋(píng)果官方文檔說(shuō)明


    -c563
  1. 顧名思義, 該方法肯定是從cls里查找是否有sel類方法,查找它的源碼
    class_getClassMethod方法實(shí)現(xiàn)-c563

這是說(shuō), 會(huì)從我們傳進(jìn)來(lái)的類的元類中查找元類的實(shí)例方法, 變相的也佐證了我們上面說(shuō)過(guò)的一句話:在底層, 沒(méi)有類方法, 都是實(shí)例方法.

  1. 這里有個(gè)坑點(diǎn)->cls->getMeta(), 我們來(lái)看它的實(shí)現(xiàn):
    -c563

這里是說(shuō)當(dāng)前cls是元類時(shí), 直接返回自身, 否則會(huì)調(diào)用它的isa獲取它的元類.
這里是因?yàn)閕sa元類走向, 最終會(huì)指向根元類并一直循環(huán)指向根元類, 這么寫(xiě)就是為了防止死循環(huán).

  1. 看到這里, 一步一步來(lái)分析, 很容易就得出結(jié)果:

method1: 類 -> 元類 -> 元類(isMetaClass返回自身)中沒(méi)有sayHello實(shí)例方法.
method2: 元類-> 元類(isMetaClass返回自身)中沒(méi)有sayHello實(shí)例方法.
method3: 類 -> 元類 -> 元類(isMetaClass返回自身)中有sayHappy類方法
method4: 元類 -> 元類(isMetaClass返回自身)中有sayHappy類方法

  1. 打印驗(yàn)證


    -c677

3: 探索class_getMethodImplementation

初始化類對(duì)象方式:
1: Class pClass = LLMethodClass.class;
or
2: Class pClass = object_getClass(person);
Class metaClass = objc_getMetaClass(className);
// - (void)sayHello;
// + (void)sayHappy;
Method method1 = class_getMethodImplementation(pClass, @selector(sayHello));
Method method2 = class_getMethodImplementation(metaClass, @selector(sayHello));

Method method3 = class_getMethodImplementation(pClass, @selector(sayHappy));
Method method4 = class_getMethodImplementation(metaClass, @selector(sayHappy));
class_getMethodImplementation實(shí)現(xiàn)-c749
***********************************************************************
* lookUpImpOrForward.
* //標(biāo)準(zhǔn)的IMP查找。
* The standard IMP lookup. 
* //沒(méi)有LOOKUP_INITIALIZE:試圖避免+initialize(但有時(shí)失敗)
* Without LOOKUP_INITIALIZE: tries to avoid +initialize (but sometimes fails)
* //沒(méi)有LOOKUP_CACHE:跳過(guò)樂(lè)觀解鎖查找(但在其他地方使用緩存)
* Without LOOKUP_CACHE: skips optimistic unlocked lookup (but uses cache elsewhere)
* //大多數(shù)調(diào)用者應(yīng)該使用LOOKUP_INITIALIZE和LOOKUP_CACHE
* Most callers should use LOOKUP_INITIALIZE and LOOKUP_CACHE
****** //inst是cls的實(shí)例或子類,如果不知道,則為nil。
****** inst is an instance of cls or a subclass thereof, or nil if none is known. 
* //如果cls是一個(gè)未初始化的元類,那么一個(gè)非nil inst會(huì)更快
* If cls is an un-initialized metaclass then a non-nil inst is faster.
* //可能返回_objc_msgForward_impcache。外用IMPs
* May return _objc_msgForward_impcache. IMPs destined for external use 
* //必須轉(zhuǎn)換為_(kāi)objc_msgForward或_objc_msgForward_stret。
* must be converted to _objc_msgForward or _objc_msgForward_stret.
* //如果你根本不想轉(zhuǎn)發(fā),使用LOOKUP_NIL
* If you don't want forwarding at all, use LOOKUP_NIL.
**********************************************************************/
lookUpImpOrForward實(shí)現(xiàn)-c749
  1. class_getMethodImplementation實(shí)際是從cls中的cache_t緩存中查找sel的實(shí)現(xiàn), 查找到返回imp的地址, 沒(méi)有查找到就會(huì)返回一個(gè)msg_forward_name的地址(objc中沒(méi)有,在llvm中查找到了msg_forward_name, 后續(xù)看不懂, 有時(shí)間再看)

  2. 看到這里, 一步一步來(lái)分析, 很容易就得出結(jié)果:

    method1: 類中有sayHello實(shí)例方法, 所以也查找到它的imp
    method2: 元類中沒(méi)有sayHello實(shí)例方法, 返回msg_forward_name地址
    method3: 類中沒(méi)有sayHappy類方法, 返回msg_forward_name地址
    method4: 元類有sayHappy類方法, 所以也查找到它的imp

  3. 打印驗(yàn)證

    class_getMethodImplementation驗(yàn)證-c747

    imp2imp3的地址是一樣的, 都是消息轉(zhuǎn)發(fā)后的imp實(shí)現(xiàn), 具體實(shí)現(xiàn)的是, 暫不得而知.

2: 探索isKindOfClassisMemberOfClass

1: isKindOfClass探索

isKindOfClass圖示1-c600
isKindOfClass圖示2-c600
  • 圖示1兩個(gè)方法都是偽實(shí)現(xiàn),斷點(diǎn)并不走這里, 全局搜索下, 只有這個(gè)實(shí)現(xiàn), 斷點(diǎn)在這里驗(yàn)證

  • 圖示2是最終走向(類方法和實(shí)例方法), 斷點(diǎn)會(huì)停到這里, 當(dāng)環(huán)境為 OBJC2時(shí)進(jìn)入斷點(diǎn)處, 正常環(huán)境則會(huì)進(jìn)入msg_send圖示1.

  • 為什么會(huì)走到里: 因?yàn)?code>編譯時(shí)就確定的原因, 所以去llvm中查找, 發(fā)現(xiàn)和alloc的消息轉(zhuǎn)發(fā)定義在了一起, so 騷操作, 你懂得...

  • 作用: 通過(guò)查找obj的元類, 并遞歸循環(huán)元類的superclass與otherClass進(jìn)行對(duì)比

    該方法中, 需要注意isa也就是元類的繼承關(guān)系, 是的元類也存在繼承關(guān)系:
    元類 -> 根元類(metaClass) -> 根類(NSObject) -> nil

isa走向-c600

2: isKindOfClass探索

isMemberOfClass圖示-c382
  • 作用:
    • +isMemberOfClass: 通過(guò)與查找自身的元類, 并與自身進(jìn)行對(duì)比
    • -isMemberOfClass: 通過(guò)與查找自身的類型, 并與自身進(jìn)行對(duì)比
?著作權(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)容