消息转发流程

Posted WeaterMr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了消息转发流程相关的知识,希望对你有一定的参考价值。

消息转发流程

forwardingTargetForSelector 快速转发

- (id)forwardingTargetForSelector:(SEL)aSelector
    NSLog(@"%s - %@",__func__,NSStringFromSelector(aSelector));
    return [Good new];

解析

  • 当对象goodOne中没有对应的方法实现,可以通过上面这个方法进行重定向,找一个对象顶替goodOne去实现对应调用的方法。
  • 方法名称要保持一致。

methodSignatureForSelector消息慢速转发

 慢速转发
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
    NSLog(@"%s - %@",__func__,NSStringFromSelector(aSelector));
    if (aSelector == @selector(name)) 
        return [NSMethodSignature signatureWithObjCTypes:"v@:@"];
    
    return [super methodSignatureForSelector:aSelector];

- (void)forwardInvocation:(NSInvocation *)anInvocation
//    NSLog(@"%@ - %@",anInvocation.target,NSStringFromSelector(anInvocation.selector));
    Good *s = [Good alloc];
    if ([self respondsToSelector:anInvocation.selector]) 
        [anInvocation invoke];
    else if ([s respondsToSelector:anInvocation.selector])
        [anInvocation invokeWithTarget:s];
    else
        NSLog(@"%s - %@",__func__,NSStringFromSelector(anInvocation.selector));
    


直接通过 forwardInvocation 将方法重定向到别的对象的方法
- (void)forwardInvocation:(NSInvocation *)anInvocation  
     Good *s = [Good alloc];
     anInvocation.target = s;
     anInvocation.selector = @selector(tell1111);
     [anInvocation invoke];

解析

  • methodSignatureForSelector 方法主要是返回对应的方法签名
  • forwardInvocation 主要是设置方法的执行对象与方法的实现。

反汇编研究转发流程

准备

下载Hopper反汇编的工具(打开选择demo)
通过hopper 打开CoreFoundation的可执行文件,去查找__forwarding_prep_0___,CoreFoundation的可执行文件怎么获取

loc_649bb:
    stack[-336] = r13;
    stack[-320] = r12;
    stack[-352] = rsi;
    rax = object_getClass(rbx);
    r12 = rax;
    r13 = class_getName(rax);
    if (class_respondsToSelector(r12, @selector(forwardingTargetForSelector:)) == 0x0) goto loc_64a67;

loc_649fc:
    rdi = rbx;
    rax = [rdi forwardingTargetForSelector:stack[-328]];
    if ((rax == 0x0) || (rax == rbx)) goto loc_64a67;

loc_64a19:

loc_64a8a:
    rax = class_respondsToSelector(r12, @selector(methodSignatureForSelector:));
    r14 = stack[-320];
    stack[-336] = r15;
    if (rax == 0x0) goto loc_64dd7;

loc_64ab2:
    rax = [r14 methodSignatureForSelector:stack[-328]];
    rbx = stack[-352];
    if (rax == 0x0) goto loc_64e3c;

loc_64ad5:

loc_64ad5:
    r12 = rax;
    rax = [rax _frameDescriptor];
    r13 = rax;
    if (((*(int16_t *)(*rax + 0x22) & 0xffff) >> 0x6 & 0x1) != rbx) 
            rax = sel_getName(stack[-328]);
            _CFLog(0x4, @"*** NSForwarding: warning: method signature and compiler disagree on struct-return-edness of '%s'.  Signature thinks it does%s return a struct, and compiler thinks it does%s.", rax);
    
    rax = object_getClass(r14);
    rax = class_respondsToSelector(rax, @selector(_forwardStackInvocation:));
    stack[-344] = r13;
    if (rax == 0x0) goto loc_64c19;

loc_64b6c:

loc_64c19:
    if (class_respondsToSelector(object_getClass(r14), @selector(forwardInvocation:)) == 0x0) goto loc_64ec2;

loc_64c3b:
    rax = [NSInvocation _invocationWithMethodSignature:r12 frame:stack[-336]];
    r13 = rax;
    [r14 forwardInvocation:rax];
    stack[-328] = 0x0;
    r14 = 0x0;
    goto loc_64c76;

loc_64ec2:

结论

  • 快速转发:通过forwardingTargetForSelector实现,指定对象接收这个消息,就会走该对象的方法查找流程,如果返回nil,进入慢速转发流程
  • 慢速转发:通过methodSignatureForSelectorforwardInvocation共同实现,如果methodSignatureForSelector返回值是nil,慢速查找流程结束,如果有返回值forwardInvocation不会崩溃。

以上是关于消息转发流程的主要内容,如果未能解决你的问题,请参考以下文章

消息转发流程(上)

iOS开发底层之消息的快速与慢速转发 - 11

iOS开发底层之消息的快速与慢速转发 - 11

iOS底层开发消息发送与转发流程

iOS底层探索之Runtime: 消息转发

第12条:理解消息转发机制