经典错误: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这个方法,那么来到这个里面,我们就可以为所欲为,即使我们什么也不做,运行程序也不会崩溃了,我们可以在这个方法里面为方法指定新的调用者,也即是进行消息转发,也可以做一些其他的操作,都可以,这就是这样设计的一个好处,我们可以在这个方法里面做一切我们想做的
网友评论