美文网首页runtime
iOS Runtime学习(四) -- 消息转发时每个步骤需要注

iOS Runtime学习(四) -- 消息转发时每个步骤需要注

作者: Q海龙 | 来源:发表于2019-07-11 15:44 被阅读52次

    众所周知,runtime有三次机会让我们来挽救crash,它们分别是

    • resolveInstanceMethodresolveClassMethod
    • forwardingTargetForSelector
    • methodSignatureForSelectorforwardInvocation

    那在这三步的处理上也大不相同,下面就具体问题具体分析

    1.resolveInstanceMethod或resolveClassMethod

    objc运行时会调用resolveInstanceMethod:或者+resolveClassMethod:,让你有机会提供一个函数实现。如果你添加了函数,那运行时系统就会重新启动一次消息发送的过程,否则运行时就会移到下一步,消息转发(Message Forwarding)。

    如果在这一步打补丁的话,我们就得需要根据特定的缺失方法来操作,例如,Person对象缺少eat方法,那我们就需要在这个方法里这样做:

    + (BOOL)resolveInstanceMethod:(SEL)sel {
        if (sel == NSSelectorFromString(@"eat")) {
            class_addMethod(self, sel, (IMP)myEat, "v@:@");
            return YES;
        }
        return [super resolveInstanceMethod:sel];
    }
    
    void myEat (id self, SEL _cmd, NSString *str) {
        NSLog(@"动态添加Eat方法");
    }
    

    这样做其实局限性还是很大的,因为苹果已经禁用了热更新,所以当你知道这里有bug时,应该是在业务代码里把这个问题解决掉,而不是通过runtime,而且在这里操作的话,if判断会执行太多次,不方便。

    2.forwardingTargetForSelector
    forwardingTargetForSelector是runtime消息传发的第一步,当resolveInstanceMethod没处理,并且没有找到方法时现时,runtime就会自行resolveInstanceMethod,在这里的做法如下:

    - (id)forwardingTargetForSelector:(SEL)aSelector {
        if (aSelector == NSSelectorFromString(@"eat")) {
            ErrorPerson *person = [[ErrorPerson alloc] init];
            return person;
        }
        return [super forwardingTargetForSelector:aSelector];
    }
    

    在这里操作其实就是将Person对象的eat方法让ErrorPerson对象执行,当然ErrorPerson也必须得实现了eat方法,不然也会crash

    3.methodSignatureForSelector和forwardInvocation
    这两个方法要组合起来用,首先在methodSignatureForSelector判断当前执行的方法签名是否存在,存在则交给父类处理,不存在,则手动注册一个方法签名,当返回注册的新方法签名后,马上就会开始执行forwardInvocation,然后我们再将事件转移输出log或者什么都不做也可以,但是必须实现forwardInvocation方法

    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
        NSMethodSignature *signature = [super methodSignatureForSelector:aSelector];
        if (!signature) {
            return [NSMethodSignature signatureWithObjCTypes:"v@"];
        }
        return [super methodSignatureForSelector:aSelector];
    }
    
    - (void)forwardInvocation:(NSInvocation *)anInvocation {
        [self log];
    }
    
    - (void)log {
        NSLog(@"forwardInvocation");
    }
    

    相关文章

      网友评论

        本文标题:iOS Runtime学习(四) -- 消息转发时每个步骤需要注

        本文链接:https://www.haomeiwen.com/subject/gqtdkctx.html