runtime

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

2019-07-11  本文已影响52人  Q海龙

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

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

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");
}
上一篇下一篇

猜你喜欢

热点阅读