總覽
Objetive-C的消息發(fā)送河胎,是通過objc_msgSend來實(shí)現(xiàn)的闯袒,具體執(zhí)行過程,主要分三個(gè)階段:
- 1、消息發(fā)送政敢;
- 2其徙、動(dòng)態(tài)方法解析
- 3、消息轉(zhuǎn)發(fā)或重新簽名
消息發(fā)送
Person類有兩個(gè)方法 sayHello 和 sayBye 喷户,Student繼承Person唾那,并重寫 sayHello 方法。
@interface Person : NSObject
-(void)sayHello;
-(void)sayBye;
@end
@interface Student : Person
@end
@implementation Person
-(void)sayHello{
NSLog(@"%s",__func__);
}
-(void)sayBye{
NSLog(@"%s",__func__);
}
@end
@implementation Student
-(void)sayHello{
NSLog(@"%s",__func__);
}
@end
現(xiàn)在褪尝,通過給Student實(shí)例對(duì)象發(fā)消息,來展示消息的調(diào)用順序
//1.1 如果接收者類的cache中能找到方法避诽,則直接調(diào)用沙庐。
//否則從接受者類的方法列表中查找方法,找到后添加到cache中
Student* student = [[Student alloc] init];
[student sayHello];
//1.2 以上兩個(gè)步驟均找不到的時(shí)候佳吞,從superClass的cache中查找轨功,同 1.1
[student sayBye];
結(jié)果如下:
2019-01-23 19:09:00.825000+0800 runtime_objc_msgSend[14364:5870808] -[Student sayHello]
2019-01-23 19:09:00.825100+0800 runtime_objc_msgSend[14364:5870808] -[Person sayBye]
通過對(duì)結(jié)果的分析,我們得到如下方法查找的順序:
- 1 接收者首先從接收者類的cache中查找方法
- 1.1 如果能找到方法容达,直接調(diào)用古涧,結(jié)束
- 1.2 如果找不到方法,繼續(xù)執(zhí)行2
- 2 從接收者類的方法列表中查找
- 2.1 如果找到方法花盐,調(diào)用并將方法添加到接收者類的cache中羡滑,結(jié)束
- 2.2 如果找不到方法,則從其superClass的cache中查找
- 遞歸2.1算芯,直到最頂層類柒昏。
- 3 如果找不到方法职祷,則判斷走以下兩個(gè)步驟
- 3.1 如果兩個(gè)步驟均不涉及,則直接拋出異常 'unrecognized selector sent to instance'
- 詳細(xì)步驟參照以下階段
- 注:每個(gè)階段結(jié)束會(huì)重新進(jìn)入本階段届囚。
動(dòng)態(tài)方法解析
如果消息發(fā)送階段有梆,未找到匹配的方法,則開發(fā)者可以通過重寫NSObject中的以下兩個(gè)方法來對(duì)未匹配的方法進(jìn)行解析意系。
+ (BOOL)resolveClassMethod:(SEL)sel
+ (BOOL)resolveInstanceMethod:(SEL)sel
為了測(cè)試代碼泥耀,我們重寫Student類兜辞,對(duì)其進(jìn)行擴(kuò)展,當(dāng)方法dynamicAnalysisMethod不存在時(shí)夸溶,我們將Student類中方法dynamicAnalysisOther的實(shí)現(xiàn)添加給dynamicAnalysisMethod逸吵。代碼如下
@interface Student : Person
-(void)dynamicAnalysisMethod;
@end
@implementation Student
+(BOOL)resolveInstanceMethod:(SEL)sel{
if (sel == @selector(dynamicAnalysisMethod)) {
Method method = class_getInstanceMethod([self class], @selector(dynamicAnalysisOther));
//Adds a new method to a class with a given name and implementation.
class_addMethod([self class],
sel,
method_getImplementation(method),
method_getTypeEncoding(method));
return true;
}
return [super resolveInstanceMethod:sel];
}
-(void)dynamicAnalysisOther{
NSLog(@"%s",__func__);
}
@end
此時(shí),我們給Student實(shí)例對(duì)象發(fā)dynamicAnalysisMethod消息缝裁,代碼如下
Student* student = [[Student alloc] init];
//針對(duì)類和實(shí)例對(duì)象方法胁塞。
//2.1重寫NSObject的方法 + (BOOL)resolveClassMethod:(SEL)sel
// 或 + (BOOL)resolveInstanceMethod:(SEL)sel
//2.2在方法中對(duì)方法進(jìn)行動(dòng)態(tài)解析。
[student dynamicAnalysisMethod];
結(jié)果如下:
2019-01-23 19:09:00.825300+0800 runtime_objc_msgSend[14364:5870808] -[Student dynamicAnalysisOther]
這樣压语,我們實(shí)現(xiàn)了消息的動(dòng)態(tài)解析。
- 針對(duì)未匹配的方法编检,我們可以通過 class_addMethod 給類添加新的方法和實(shí)現(xiàn)
- 重新進(jìn)入消息發(fā)送階段
消息轉(zhuǎn)發(fā)
如果在以上兩個(gè)階段均沒有找到相關(guān)方法胎食,此時(shí)就進(jìn)入了消息轉(zhuǎn)發(fā)階段。消息轉(zhuǎn)發(fā)主要有兩個(gè)類別
- 直接轉(zhuǎn)發(fā)
- 方法重簽名允懂,轉(zhuǎn)發(fā)
此時(shí)厕怜,我們新建一個(gè)Worker類,詳細(xì)代碼如下:
@interface Worker : NSObject
-(void)sayHello;
-(void)reSignature;
@end
@implementation Worker
-(id)forwardingTargetForSelector:(SEL)aSelector{
if (aSelector == @selector(sayHello)) {
return [[Student alloc] init];
}
return nil;
}
-(NSMethodSignature*)methodSignatureForSelector:(SEL)sel{
if (sel == @selector(reSignature)) {
NSMethodSignature* signature = [[[Student alloc]
init]
methodSignatureForSelector:@selector(reSignatureMethod)];
return signature;
}
return [super methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation{
NSLog(@"%s",__func__);
if (anInvocation.selector == @selector(reSignatureMethod)) {
anInvocation.target = [[Student alloc] init];
[anInvocation invoke];
//或者如下形式
//[anInvocation invokeWithTarget:[[Student alloc] init]];
}
}
@end
對(duì)Worker類蕾总,有兩個(gè)方法申明sayHello 和reSignature 粥航, 但是并不對(duì)其進(jìn)行實(shí)現(xiàn)。此時(shí)我們給Worker的實(shí)例對(duì)象發(fā)送消息生百,如下:
Worker* worker = [[Worker alloc] init];
//直接轉(zhuǎn)發(fā)
//3.1 重寫NSObject的方法 - (id)forwardingTargetForSelector:(SEL)aSelector
//返回 消息接收者對(duì)象
[worker sayHello];
//方法重簽名递雀。
//如果3.1轉(zhuǎn)發(fā)方法返回的是nil。則可以通過重新簽名的方式來實(shí)現(xiàn)
//4.1 重寫NSObject的類/實(shí)例方法
// - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
// 或 + (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector
//4.2 在方法中蚀浆,返回新方法的方法簽名
//4.3 重寫NSObject的方法 - (void)forwardInvocation:(NSInvocation *)anInvocation
//根據(jù)簽名等信息缀程,對(duì)NSInvocation的target進(jìn)行賦值。然后invoke喚醒
[worker reSignature];
方法均沒有實(shí)現(xiàn)市俊,我們通過消息轉(zhuǎn)發(fā)杨凑,結(jié)果如下:
2019-01-23 19:09:00.825449+0800 runtime_objc_msgSend[14364:5870808] -[Student sayHello]
2019-01-23 19:09:00.825554+0800 runtime_objc_msgSend[14364:5870808] -[Worker forwardInvocation:]
消息直接轉(zhuǎn)發(fā)
- 重寫NSObject的 -(id)forwardingTargetForSelector:(SEL)aSelector方法
- 直接返回接收消息的對(duì)象實(shí)例。
方法重新簽名
-
通過重寫NSObject的方法
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector + (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector
在方法中摆昧,我們針對(duì)reSignature selector進(jìn)行了重新簽名
-
重寫NSObject方法
- (void)forwardInvocation:(NSInvocation *)anInvocation
方法中撩满,對(duì) reSignatureMethod selector的target進(jìn)行了重新賦值
喚醒
進(jìn)入方法發(fā)送階段