總覽
Objetive-C的消息發(fā)送,是通過(guò)objc_msgSend來(lái)實(shí)現(xiàn)的,具體執(zhí)行過(guò)程,主要分三個(gè)階段:
- 1、消息發(fā)送;
- 2、動(dòng)態(tài)方法解析
- 3、消息轉(zhuǎn)發(fā)或重新簽名
消息發(fā)送
Person類(lèi)有兩個(gè)方法 sayHello 和 sayBye ,Student繼承Person,并重寫(xiě) 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)在,通過(guò)給Student實(shí)例對(duì)象發(fā)消息,來(lái)展示消息的調(diào)用順序
//1.1 如果接收者類(lèi)的cache中能找到方法,則直接調(diào)用。
//否則從接受者類(lèi)的方法列表中查找方法,找到后添加到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]
通過(guò)對(duì)結(jié)果的分析,我們得到如下方法查找的順序:
- 1 接收者首先從接收者類(lèi)的cache中查找方法
- 1.1 如果能找到方法,直接調(diào)用,結(jié)束
- 1.2 如果找不到方法,繼續(xù)執(zhí)行2
- 2 從接收者類(lèi)的方法列表中查找
- 2.1 如果找到方法,調(diào)用并將方法添加到接收者類(lèi)的cache中,結(jié)束
- 2.2 如果找不到方法,則從其superClass的cache中查找
- 遞歸2.1,直到最頂層類(lèi)。
- 3 如果找不到方法,則判斷走以下兩個(gè)步驟
- 3.1 如果兩個(gè)步驟均不涉及,則直接拋出異常 'unrecognized selector sent to instance'
- 詳細(xì)步驟參照以下階段
- 注:每個(gè)階段結(jié)束會(huì)重新進(jìn)入本階段。
動(dòng)態(tài)方法解析
如果消息發(fā)送階段,未找到匹配的方法,則開(kāi)發(fā)者可以通過(guò)重寫(xiě)NSObject中的以下兩個(gè)方法來(lái)對(duì)未匹配的方法進(jìn)行解析。
+ (BOOL)resolveClassMethod:(SEL)sel
+ (BOOL)resolveInstanceMethod:(SEL)sel
為了測(cè)試代碼,我們重寫(xiě)Student類(lèi),對(duì)其進(jìn)行擴(kuò)展,當(dāng)方法dynamicAnalysisMethod不存在時(shí),我們將Student類(lèi)中方法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ì)類(lèi)和實(shí)例對(duì)象方法。
//2.1重寫(xiě)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ì)未匹配的方法,我們可以通過(guò) class_addMethod 給類(lèi)添加新的方法和實(shí)現(xiàn)
- 重新進(jìn)入消息發(fā)送階段
消息轉(zhuǎn)發(fā)
如果在以上兩個(gè)階段均沒(méi)有找到相關(guān)方法,此時(shí)就進(jìn)入了消息轉(zhuǎn)發(fā)階段。消息轉(zhuǎn)發(fā)主要有兩個(gè)類(lèi)別
- 直接轉(zhuǎn)發(fā)
- 方法重簽名,轉(zhuǎn)發(fā)
此時(shí),我們新建一個(gè)Worker類(lèi),詳細(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類(lèi),有兩個(gè)方法申明sayHello 和reSignature , 但是并不對(duì)其進(jìn)行實(shí)現(xiàn)。此時(shí)我們給Worker的實(shí)例對(duì)象發(fā)送消息,如下:
Worker* worker = [[Worker alloc] init];
//直接轉(zhuǎn)發(fā)
//3.1 重寫(xiě)NSObject的方法 - (id)forwardingTargetForSelector:(SEL)aSelector
//返回 消息接收者對(duì)象
[worker sayHello];
//方法重簽名。
//如果3.1轉(zhuǎn)發(fā)方法返回的是nil。則可以通過(guò)重新簽名的方式來(lái)實(shí)現(xiàn)
//4.1 重寫(xiě)NSObject的類(lèi)/實(shí)例方法
// - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
// 或 + (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector
//4.2 在方法中,返回新方法的方法簽名
//4.3 重寫(xiě)NSObject的方法 - (void)forwardInvocation:(NSInvocation *)anInvocation
//根據(jù)簽名等信息,對(duì)NSInvocation的target進(jìn)行賦值。然后invoke喚醒
[worker reSignature];
方法均沒(méi)有實(shí)現(xiàn),我們通過(guò)消息轉(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ā)
- 重寫(xiě)NSObject的 -(id)forwardingTargetForSelector:(SEL)aSelector方法
- 直接返回接收消息的對(duì)象實(shí)例。
方法重新簽名
-
通過(guò)重寫(xiě)NSObject的方法
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector + (NSMethodSignature *)instanceMethodSignatureForSelector:(SEL)aSelector在方法中,我們針對(duì)reSignature selector進(jìn)行了重新簽名
-
重寫(xiě)NSObject方法
- (void)forwardInvocation:(NSInvocation *)anInvocation方法中,對(duì) reSignatureMethod selector的target進(jìn)行了重新賦值
喚醒
進(jìn)入方法發(fā)送階段