接上篇内容,若是方法的调用,没有实现动态方法决议,那么就会进行消息转发。消费转发分为两个阶段分别以下:sass
要实现快速转发须要在未实现实现动态方法决议的状况下,实现下面函数:markdown
//将方法的调用转给其余对象(本身作不了,就交给其余人作 )
-(id)forwardingTargetForSelector:(SEL)aSelector
+(id)forwardingTargetForSelector:(SEL)aSelector
复制代码
代码演示:函数
#import <Foundation/Foundation.h>
@interface ABPerson : NSObject
@end
@implementation ABPerson
- (void)doSomething
{
NSLog(@"%s",__func__);
}
@end
@interface ABPeople : NSObject
- (void)doSomething;
@end
@implementation ABPeople
-(id)forwardingTargetForSelector:(SEL)aSelector{
NSLog(@"%s-%@",__func__,NSStringFromSelector(aSelector));
return [ABPerson alloc];
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
ABPeople *p = [ABPeople new];
[p doSomething];
NSLog(@"Hello, World!");
}
return 0;
}
复制代码
ABPeople
没有实现的方法交给了ABPerson
实现并调用。可是若是快速转发也没有实现呢?那就进行慢速转发。post
//经过方法编号,返回方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
+ (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
复制代码
//调用者Target,方法sel都被保存在anInvocation中
-(void)forwardInvocation:(NSInvocation *)anInvocation
+(void)forwardInvocation:(NSInvocation *)anInvocation
复制代码
这两个方法必须同时实现,forwardInvocation
能够实现空方法,也不会报错。ui
代码演示:spa
#import <Foundation/Foundation.h>
@interface ABPeople : NSObject
- (void)doSomething;
@end
@implementation ABPeople
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
NSLog(@"%s-%@",__func__,NSStringFromSelector(aSelector));
if (aSelector == @selector(doSomething)) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}
return [super methodSignatureForSelector:aSelector];
}
-(void)forwardInvocation:(NSInvocation *)anInvocation
{
NSLog(@"%@- %@",anInvocation.target,NSStringFromSelector(anInvocation.selector));
ABPerson *p = [ABPerson new];
if ([p respondsToSelector:anInvocation.selector]) {
anInvocation.target = p;
[anInvocation invoke];
}
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
ABPeople *p = [ABPeople new];
[p doSomething];
NSLog(@"Hello, World!");
}
return 0;
}
复制代码
在方法找不到,崩溃的时候,咱们经过bt
命令打印调用栈:调试
在找不到方法的最后处理
doesNotRecognizeSelector
,执行了红框中的东西,能看出它们来自CoreFoundation
。code
将CoreFoundation
可执行文件拖到Hopper Disassemalber
,查看反汇编伪代码。orm
全局搜索forwarding
对象
___forwarding_prep_0___
调用了 ____forwarding___
,查看 ____forwarding___
实现:
int ____forwarding___(int arg0, int arg1) {
省略部分伪代码
loc_649bb:
var_148 = r13;
var_138 = r12;
var_158 = rsi;
rax = object_getClass(rbx);
r12 = rax;
r13 = class_getName(rax);
//当类没有实现forwardingTargetForSelector,就执行loc_64a67
if (class_respondsToSelector(r12, @selector(forwardingTargetForSelector:)) == 0x0) goto loc_64a67;
loc_649fc:
rdi = rbx;
//当类实现了forwardingTargetForSelector,就返回rax(对象)
rax = [rdi forwardingTargetForSelector:var_140];
//若是rax不存在或等于原来的对象就就执行loc_64a67
if ((rax == 0x0) || (rax == rbx)) goto loc_64a67;
省略部分伪代码
loc_64a67:
var_138 = rbx;
//判断是不是僵尸对象
if (strncmp(r13, "_NSZombie_", 0xa) == 0x0) goto loc_64dc1;
loc_64a8a:
//当类没有实现methodSignatureForSelector,就执行loc_64dd7
rax = class_respondsToSelector(r12, @selector(methodSignatureForSelector:));
r14 = var_138;
var_148 = r15;
if (rax == 0x0) goto loc_64dd7;
loc_64ab2:
//当类实现了methodSignatureForSelector,就返回rax(对象)
rax = [r14 methodSignatureForSelector:var_140];
rbx = var_158;
//没有实现就执行loc_64e3c
if (rax == 0x0) goto loc_64e3c;
省略部分伪代码
//系统实现的方法_forwardStackInvocation,并无对外暴露
rax = class_respondsToSelector(rax, @selector(_forwardStackInvocation:));
var_150 = r13;
if (rax == 0x0) goto loc_64c19;
loc_64b6c:
if (*0x5c2700 != 0xffffffffffffffff) {
dispatch_once(0x5c2700, ^ {/* block implemented at ______forwarding____block_invoke */ } });
}
//NSInvocation的一些处理
r15 = [NSInvocation requiredStackSizeForSignature:r12];
rsi = *0x5c26f8;
rsp = rsp - ___chkstk_darwin(@class(NSInvocation), rsi, r12, rcx);
r13 = &stack[-360];
__bzero(r13, rsi);
___chkstk_darwin(r13, rsi, r12, rcx);
rax = objc_constructInstance(*0x5c26f0, r13);
var_140 = r15;
[r13 _initWithMethodSignature:r12 frame:var_148 buffer:&stack[-360] size:r15];
[var_138 _forwardStackInvocation:r13];
r14 = 0x1;
goto loc_64c76;
省略部分伪代码
loc_64c19:
//类是否能响应forwardInvocation,不能响应就执行loc_64ec2
if (class_respondsToSelector(object_getClass(r14), @selector(forwardInvocation:)) == 0x0) goto loc_64ec2;
loc_64c3b:
rax = [NSInvocation _invocationWithMethodSignature:r12 frame:var_148];
r13 = rax;
//开始调用
[r14 forwardInvocation:rax];
var_140 = 0x0;
r14 = 0x0;
goto loc_64c76;
loc_64ec2:
rdi = &var_130;
____forwarding___.cold.3(rdi, r14);
goto loc_64ed1;
复制代码
消息转发流程(经过sel
寻找imp
)以下:
lookUpImpOrForward
慢速查找没有找到imp
resolveMethod_locked
判断当前class
是否是类resolveClassMethod
执行类动态方法决议resolveInstanceMethod
执行对象动态方法决议forwardingTargetForSelector
methodSignatureForSelector
返回签名,forwardInvocation
调用执行doesNotRecognizeSelector
当方法为实现时,动态方法决议会打印两遍
objc
工程调试:
在此文件函数内打上断点
在第二次即将输出当前
sel
的时候,打印函数调用栈
缘由是CoreFoundation
调用了__forwarding_prep_0___
,通过系统处理调用了class_respondsToSelector_inst
。调用流程以下:
__forwarding_prep_0___
->class_respondsToSelector_inst
->lookUpImpOrNilTryCache
->lookUpImpOrForward
。
这样作的目的是在消息转发过程当中,若是imp
被加上了,能够再次获得处理。