runtime之動態(tài)添加方法

如果一個(gè)類接收到一個(gè)沒有實(shí)現(xiàn)的實(shí)例方法,就會進(jìn)到下面這個(gè)方法里+(BOOL)resolveInstanceMethod:(SEL)sel,同理類方法會進(jìn)到這個(gè)+(BOOL)resolveClassMethod:(SEL)sel。

1. 驗(yàn)證

首先創(chuàng)建一個(gè)Student類然后調(diào)用一個(gè)沒實(shí)現(xiàn)的learn方法:

    Student *s = [[Student alloc] init];
    [s performSelector:@selector(learn)];

然后在Student.m中攔截一下,打印方法名:

// 如果該類接收到一個(gè)沒有實(shí)現(xiàn)的實(shí)例方法,就會進(jìn)到這個(gè)方法里
+(BOOL)resolveInstanceMethod:(SEL)sel {
    NSLog(@"%@",NSStringFromSelector(sel));
    return [super resolveInstanceMethod:sel];
}

打印

runtime_動態(tài)添加方法[2398:288368] learn

2. 動態(tài)添加方法

接下來,攔截到以后動態(tài)添加一個(gè)方法,用runtime的class_addMethod方法

// 動態(tài)添加方法
    /*
     1、cls 給哪個(gè)類添加方法
     2、SEL 方法編號
     3、IMP 函數(shù)指針
     4、返回值類型
     */
    class_addMethod(<#Class  _Nullable __unsafe_unretained cls#>, <#SEL  _Nonnull name#>, <#IMP  _Nonnull imp#>, <#const char * _Nullable types#>)

前邊三個(gè)參數(shù)都好理解,關(guān)于第四個(gè)參數(shù),蘋果的文檔里有介紹,command+shift+0打開文檔,搜索class_addMethod,下面是示例代碼和截圖

class_addMethod([self class], @selector(resolveThisMethodDynamically), (IMP) myMethodIMP, "v@:");
image.png

然后在resolveInstanceMethod動態(tài)添加一個(gè)方法,在用speak方法接收處理:

+(BOOL)resolveInstanceMethod:(SEL)sel {
    
    NSLog(@"%@",NSStringFromSelector(sel));
    // 動態(tài)添加方法
    class_addMethod(self, sel, (IMP)speak, "v@:");
    
    return [super resolveInstanceMethod:sel];
}

void speak() {
    NSLog(@"speak");
}

打印

runtime_動態(tài)添加方法[2430:292288] learn
runtime_動態(tài)添加方法[2430:292288] speak

動態(tài)創(chuàng)建方法成功

3. 傳參數(shù)

OC的方法調(diào)用,會默認(rèn)傳遞兩個(gè)隱式參數(shù)給IMP(方法實(shí)現(xiàn)),文檔里有示例代碼,截圖


image.png

首先調(diào)用時(shí)傳個(gè)參數(shù):

    Student *s = [[Student alloc] init];
    [s performSelector:@selector(learn) withObject:@"English"];

也可以換成發(fā)送消息(這里不懂可以看我之前的關(guān)于消息轉(zhuǎn)發(fā)的簡書),這里的參數(shù)和我們接收時(shí)的參數(shù)是一一對應(yīng)的,便于理解

    Student *s = [[Student alloc] init];
    objc_msgSend(s, sel_registerName("learn"), @"English");

這里class_addMethod函數(shù)的第四個(gè)參數(shù)需要改成"v@:@",接收的speak函數(shù)需要把隱式參數(shù)都加上,才可以收到我們傳的參數(shù):

// 如果該類接收到一個(gè)沒有實(shí)現(xiàn)的實(shí)例方法,就會進(jìn)到這個(gè)方法里
+(BOOL)resolveInstanceMethod:(SEL)sel {
    
    NSLog(@"%@",NSStringFromSelector(sel));
    // 動態(tài)添加方法
    class_addMethod(self, sel, (IMP)speak, "v@:@");
    
    return [super resolveInstanceMethod:sel];
}

/**
 OC的方法調(diào)用,會默認(rèn)傳遞兩個(gè)隱式參數(shù)給IMP(方法實(shí)現(xiàn))
 objc_msgSend(self,_cmd)
 id self  方法的調(diào)用者
 SEL _cmd 方法編號
 */
void speak(id class, SEL sel, NSString *objc) {
    NSLog(@"%@-%@-%@",NSStringFromClass([class class]),NSStringFromSelector(sel),objc);
}

打印

runtime_動態(tài)添加方法[2458:295930] learn
runtime_動態(tài)添加方法[2458:295930] Student-learn-English

對照一下發(fā)消息和接收函數(shù)的參數(shù):

// 發(fā)消息
objc_msgSend(s, sel_registerName("learn"), @"English");
// 接收函數(shù)
void speak(id class, SEL sel, NSString *objc)

完?。?!

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

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

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