3-2 runtime-消息传递机制

经典错误:unrecognized selector sent to instance 0x60400000e3e0。

程序崩溃很容易理解,因为在第一步查找方法中,在自己的类对象以及父类的类对象中都没有找到这个方法,所以转向动态方法解析,动态方法解析我们什么也没做,所以会转向消息转发,消息转发我们也什么都没做,所以最后产生崩溃。接下来我们实现一下动态方法解析。

方法发送流程

1.判断receiver 是否为空 
2.不为空从 receiverClass 的cache 里面查找
3.没有 从receiverClass的方法列表查找
4.没有 从父类的 cache  里面查找
5.没有 从receiverClass 父类 的方法列表查找
6.一直找 直到没有父类
7.都没有那么就会走方法解析

方法解析

是对象方法时,调用 resolveInstanceMethod:方法
当调用的是类方法时,调用resolveClassMethod:方法

/////> 第一次纠错 动态方法解析
+ (BOOL)resolveInstanceMethod:(SEL)sel{
    if (sel == @selector(studentTest)) {
        Method method = class_getInstanceMethod(self, @selector(test2));
        class_addMethod(self, sel, method_getImplementation(method), "v16@0:8");
        return YES;
    }
    
    return [super resolveInstanceMethod:sel];
}

- (void)test2 {
    NSLog(@"test2");
}

消息转发 消息转发通俗地讲就是本类没有能力去处理这个消息,那么就转发给其他的类,让其他类去处理

///> 第二次纠错 消息转发。转发给其他类实现
- (id)forwardingTargetForSelector:(SEL)aSelector {
    Car *car = Car.new;
    if ([car respondsToSelector:aSelector]) {
        return car;
    }
    return nil;
}







///> 第三次纠错
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{    
    if(aSelector == @selector(messageForwardingtestAge:)){
        return [NSMethodSignature signatureWithObjCTypes:"v20@0:8i16"];
    }
    return [super methodSignatureForSelector:aSelector];
}

//NSInvocation封装了一个方法调用,包括:方法调用者,方法名,方法参数
//@  anInvocation.target 方法调用者
//@   anInvocation.selector 方法名
//@   [anInvocation getArgument:NULL atIndex:0];
- (void)forwardInvocation:(NSInvocation *)anInvocation{
    NSLog(@"%@ %@", anInvocation.target, NSStringFromSelector(anInvocation.selector));
    int age;
    [anInvocation getArgument:&age atIndex:2];
    NSLog(@"messageForwarding1 %d", age);
    //这行代码是把方法的调用者改变为student对象
    // [anInvocation invokeWithTarget:[[Student alloc] init]];
}

为什么有三个函数

在第三阶段消息转发阶段为什么会有三个函数这个复杂?如果我们想要转发消息,那么直接在- (id)forwardingTargetForSelector:(SEL)aSelector去返回一个消息转发对象就可以了呀。设计三个函数的好处就是,当来到- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector这个方法时,如果这个方法返回为空,那么走到这里直接结束方法调用,产生崩溃,而如果返回不为空,那么就会继续去调用- (void)forwardInvocation:(NSInvocation *)anInvocation这个方法,那么来到这个里面,我们就可以为所欲为,即使我们什么也不做,运行程序也不会崩溃了,我们可以在这个方法里面为方法指定新的调用者,也即是进行消息转发,也可以做一些其他的操作,都可以,这就是这样设计的一个好处,我们可以在这个方法里面做一切我们想做的

你可能感兴趣的:(3-2 runtime-消息传递机制)