問題:Runtime如何通過selector找到對應(yīng)的IMP地址莺奔?(分別考慮實例方法和類方法)

一、概述

  • 對于實例方法变泄,每個實例的 isa 指針指向著對應(yīng)類對象令哟,而每一個類對象中都一個對象方法列表。
  • 對于類方法杖刷,每個類對象的 isa 指針都指向著對應(yīng)的元對象励饵,而每一個元對象中都有一個類方法列表。
  • 方法列表中記錄著方法的名稱滑燃,方法實現(xiàn)役听,以及參數(shù)類型,其實 selector 本質(zhì)就是方法名稱表窘,通過這個方法名稱就可以在方法列表中找到對應(yīng)的方法實現(xiàn)典予。
  • 當(dāng)我們發(fā)送一個消息給一個NSObject對象時,這條消息會在對象的類對象方法列表里查找
  • 當(dāng)我們發(fā)送一個消息給一個類時乐严,這條消息會在類的Meta Class對象的方法列表里查找
struct objc_class {
    Class isa  OBJC_ISA_AVAILABILITY;
#if !__OBJC2__
    Class super_class                                        
    const char *name                                         
    long version                                             
    long info                                                
    long instance_size                                       
    struct objc_ivar_list *ivars                             
    struct objc_method_list **methodLists                    
    struct objc_cache *cache                                 
    struct objc_protocol_list *protocols                     
#endif

} OBJC2_UNAVAILABLE;
#這里聲明了一個指向struct objc_method_list指針的指針瘤袖,可以包含類方法列表和實例方法列表

二、具體實現(xiàn)

在尋找IMP的地址時昂验,runtime提供了兩種方法:

  • IMP class_getMethodImplementation(Class cls, SEL name);
  • IMP method_getImplementation(Method m);

根據(jù)官方描述捂敌,第一種方法可能會更快一些
@note \c class_getMethodImplementation may be faster than \c method_getImplementation(class_getInstanceMethod(cls, name)).

  • 2.1、 IMP class_getMethodImplementation(Class cls, SEL name)

對于第一種方法而言既琴,類方法和實例方法實際上都是通過調(diào)用class_getMethodImplementation()來尋找IMP地址的占婉,不同之處在于傳入的第一個參數(shù)不同(假設(shè)有一個類A)

`類方法`
class_getMethodImplementation(objc_getMetaClass("A"),@selector(methodName));

`實例方法`
class_getMethodImplementation([A class],@selector(methodName));

通過該傳入的參數(shù)不同,找到不同的方法列表甫恩,方法列表中保存著下面方法的結(jié)構(gòu)體逆济,結(jié)構(gòu)體中包含這方法的實現(xiàn),selector本質(zhì)就是方法的名稱,通過該方法名稱奖慌,即可在結(jié)構(gòu)體中找到相應(yīng)的實現(xiàn)抛虫。
Selector、Method 和 IMP 的關(guān)系可以這樣描述:
在運(yùn)行期分發(fā)消息简僧,方法列表中的每一個實體都是一個方法(Method)建椰,它的名字叫做選擇器(SEL),對應(yīng)著一種方法實現(xiàn)(IMP)涎劈。

/// An opaque type that represents a method in a class definition.
typedef struct objc_method *Method;

struct objc_method {
    SEL method_name; // 方法選擇器广凸。
    char *method_types; // 存儲著方法的參數(shù)類型和返回值類型阅茶。
    IMP method_imp; // 函數(shù)指針蛛枚。
} 
  • 2.2、 IMP method_getImplementation(Method m);

而對于第二種方法而言脸哀,傳入的參數(shù)只有method蹦浦,區(qū)分類方法和實例方法在于封裝method的函數(shù)

`類方法`
Method class_getClassMethod(Class cls, SEL name)

`實例方法`
Method class_getInstanceMethod(Class cls, SEL name)

`最后調(diào)用`
IMP method_getImplementation(Method m)
`獲取IMP地址`
  • 2.3 兩種方法代碼示例

這里有一個叫Test的類,在初始化方法里撞蜂,調(diào)用了三次getIMPFromSelector:自定義方法盲镶,第一個aaa方法是不存在的,test1和test2分別為實例方法和類方法

#import "Test.h"
#import <objc/runtime.h>

@implementation Test
- (instancetype)init {
    self = [super init];
    if (self) {
        [self getIMPFromSelector:@selector(aaa)];
        [self getIMPFromSelector:@selector(test1)];
        [self getIMPFromSelector:@selector(test2)];
    }
    return self;
}

- (void)test1 {
    NSLog(@"test1");
}

+ (void)test2 {
    NSLog(@"test2");
}

- (void)getIMPFromSelector:(SEL)aSelector {
    NSLog(@"第一種 IMP class_getMethodImplementation(Class cls, SEL name)")
    NSLog(@"實例方法")
    IMP insttanceIMP1 = class_getMethodImplementation(objc_getClass("Test"), aSelector);
    NSLog(@"類方法")
    IMP classIMP1 = class_getMethodImplementation(objc_getMetaClass("Test"), aSelector);
    

    NSLog(@"第二種 IMP method_getImplementation(Method m)")
    NSLog(@"實例方法")
    Method insttanceMethod = class_getInstanceMethod(objc_getClass("Test"), aSelector);
    IMP insttanceIMP2 = method_getImplementation(insttanceMethod);
    NSLog(@"類方法")
    Method classMethod1 = class_getClassMethod(objc_getClass("Test"), aSelector);
    IMP classIMP2 = method_getImplementation(classMethod1);
    NSLog(@"類方法")
    Method classMethod2 = class_getClassMethod(objc_getMetaClass("Test"), aSelector);
    IMP classIMP3 = method_getImplementation(classMethod2);
    
    NSLog(@"insttanceIMP1:%p insttanceIMP2:%p classIMP1:%p classIMP2:%p classIMP3:%p", insttanceIMP1, insttanceIMP2, classIMP1, classIMP2, classIMP3);    
}

@end

然后我實例化了Test的對象蝌诡,打印信息如下

Test *objc = [[Test alloc] init];

insttanceIMP1:0x7fff50ba5080 insttanceIMP2:0x0         classIMP1:0x7fff50ba5080 classIMP2:0x0         classIMP3:0x0
insttanceIMP1:0x10ebbac90    insttanceIMP2:0x10ebbac90 classIMP1:0x7fff50ba5080 classIMP2:0x0         classIMP3:0x0
insttanceIMP1:0x7fff50ba5080 insttanceIMP2:0x0         classIMP1:0x10ebbacc0    classIMP2:0x10ebbacc0 classIMP3:0x10ebbacc0
  • 在調(diào)用第一種方法IMP class_getMethodImplementation(Class cls, SEL name)時溉贿,無法找到對應(yīng)實現(xiàn)時返回的相同的一個地址0x7fff50ba5080,無論該方法是在實例方法或類方法浦旱,返回的地址都是相同的(但是每次運(yùn)行該程序時返回的地址并不相同)宇色;
  • 在調(diào)用第二種方法IMP class_getMethodImplementation(Class cls, SEL name)時,如果找不到對應(yīng)的實現(xiàn)颁湖,則返回0x0宣蠕。

還有一點(diǎn)有趣的是:

  • 調(diào)用class_getClassMethod()的第一個參數(shù)無論傳入objc_getClass()還是objc_getMetaClass(),最終調(diào)用method_getImplementation()都可以成功的找到類方法的實現(xiàn)甥捺。
  • 調(diào)用class_getInstanceMethod()的第一個參數(shù)如果傳入objc_getMetaClass()抢蚀,再調(diào)用method_getImplementation()時無法找到實例方法的實現(xiàn),可以找到類方法的實現(xiàn)镰禾。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末皿曲,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子吴侦,更是在濱河造成了極大的恐慌屋休,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,539評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件妈倔,死亡現(xiàn)場離奇詭異博投,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)盯蝴,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評論 3 396
  • 文/潘曉璐 我一進(jìn)店門毅哗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來听怕,“玉大人,你說我怎么就攤上這事虑绵∧虿t!?“怎么了?”我有些...
    開封第一講書人閱讀 165,871評論 0 356
  • 文/不壞的土叔 我叫張陵翅睛,是天一觀的道長声搁。 經(jīng)常有香客問我,道長捕发,這世上最難降的妖魔是什么疏旨? 我笑而不...
    開封第一講書人閱讀 58,963評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮扎酷,結(jié)果婚禮上檐涝,老公的妹妹穿的比我還像新娘。我一直安慰自己法挨,他們只是感情好谁榜,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著凡纳,像睡著了一般窃植。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上荐糜,一...
    開封第一講書人閱讀 51,763評論 1 307
  • 那天巷怜,我揣著相機(jī)與錄音,去河邊找鬼狞尔。 笑死丛版,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的偏序。 我是一名探鬼主播页畦,決...
    沈念sama閱讀 40,468評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼研儒!你這毒婦竟也來了豫缨?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評論 0 276
  • 序言:老撾萬榮一對情侶失蹤端朵,失蹤者是張志新(化名)和其女友劉穎好芭,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體冲呢,經(jīng)...
    沈念sama閱讀 45,850評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡舍败,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評論 3 338
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片邻薯。...
    茶點(diǎn)故事閱讀 40,144評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡裙戏,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出厕诡,到底是詐尸還是另有隱情累榜,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評論 5 346
  • 正文 年R本政府宣布灵嫌,位于F島的核電站壹罚,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏寿羞。R本人自食惡果不足惜猖凛,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望稠曼。 院中可真熱鬧形病,春花似錦、人聲如沸霞幅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽司恳。三九已至,卻和暖如春绍傲,著一層夾襖步出監(jiān)牢的瞬間扔傅,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評論 1 272
  • 我被黑心中介騙來泰國打工烫饼, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留猎塞,地道東北人。 一個月前我還...
    沈念sama閱讀 48,415評論 3 373
  • 正文 我出身青樓杠纵,卻偏偏與公主長得像荠耽,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子比藻,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評論 2 355