[toc]
探索1: 方法的歸屬
通過(guò)上一節(jié), 我們學(xué)習(xí)到了
- 通過(guò)lldb和內(nèi)存地址, 從類以及元類里查找我們聲明的(
class_ro_t *)(成員變量列表),property_array_t(屬性列表) ,method_array_t(方法列表),protocol_array_t(protocol_列表)- 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));
- class_getInstanceMethod作用:
尋找實(shí)例方法
-
我們?cè)趏bjc源碼中查看它的源碼:
源碼-c545 -
這里我們分別對(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)然圖只截了一小部分, 感興趣的可以自己去看下:
- 那么聯(lián)想到這里, 我們就能大概猜到, 當(dāng)我們調(diào)用class_getInstanceMethod方法時(shí), 它在源碼里會(huì)根據(jù)傳入的cls, 來(lái)查找它的結(jié)構(gòu)體里有沒(méi)有我們要獲取的方法.
- 那我們?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è)方法的. -
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í)里. - 接下來(lái)繼續(xù)從他的超類里查找, 也是沒(méi)有的.
- 接下來(lái), 我們也就知道m(xù)ethod4和method5的結(jié)果, 在類里找類方法肯定沒(méi)有, 在元類里找類方法, 那一找一個(gè)準(zhǔn).
-
最后我們來(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));
-
蘋(píng)果官方文檔說(shuō)明
-c563
- 顧名思義, 該方法肯定是從
cls里查找是否有sel類方法,查找它的源碼
class_getClassMethod方法實(shí)現(xiàn)-c563
這是說(shuō), 會(huì)從我們傳進(jìn)來(lái)的類的元類中查找元類的實(shí)例方法, 變相的也佐證了我們上面說(shuō)過(guò)的一句話:在底層, 沒(méi)有類方法, 都是實(shí)例方法.
- 這里有個(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).
- 看到這里, 一步一步來(lái)分析, 很容易就得出結(jié)果:
method1: 類 -> 元類 -> 元類(
isMetaClass返回自身)中沒(méi)有sayHello實(shí)例方法.
method2: 元類-> 元類(isMetaClass返回自身)中沒(méi)有sayHello實(shí)例方法.
method3: 類 -> 元類 -> 元類(isMetaClass返回自身)中有sayHappy類方法
method4: 元類 -> 元類(isMetaClass返回自身)中有sayHappy類方法
-
打印驗(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));
***********************************************************************
* 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.
**********************************************************************/
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í)間再看)-
看到這里, 一步一步來(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 -
打印驗(yàn)證
class_getMethodImplementation驗(yàn)證-c747
imp2和imp3的地址是一樣的, 都是消息轉(zhuǎn)發(fā)后的imp實(shí)現(xiàn), 具體實(shí)現(xiàn)的是, 暫不得而知.
2: 探索isKindOfClass和isMemberOfClass
1: isKindOfClass探索
圖示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
2: isKindOfClass探索
-
作用:
-
+isMemberOfClass: 通過(guò)與查找自身的元類, 并與自身進(jìn)行對(duì)比 -
-isMemberOfClass: 通過(guò)與查找自身的類型, 并與自身進(jìn)行對(duì)比
-