如果一個(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@:");

然后在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)),文檔里有示例代碼,截圖

首先調(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)