iOS高级开发iOS进阶

消息转发机制

2018-03-07  本文已影响7人  泰克2008

什么时候会报 unrecognied selector异常?

1、当调用对象(子类、各级父类)中不含有对应方法的时候,并且依旧没有给出“消息转发”的具体方案的时候,程序在运行时会crash并抛出 unrecognized selector 异常
2、运行时会根据对象的isa指针找到该对象所对应的类,然后会依次在对应的类、父类、爷爷类、根类中找对应的方法

但在崩溃前,objc的运行时会给出三次拯救程序崩溃的机会,
流程图如下:

动态解析流程.png

根类NSObject提供了重定向方法,如图:

重定向方法.png

1、+ (BOOL)resolveInstanceMethod:(SEL)sel

指定是否动态添加方法。若返回NO,则进入下一步,若返回YES,则通过class_addMethod()函数动态地添加方法,消息得到处理,此流程完毕。

// 一. 动态方法解析
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    NSLog(@"sel = %@", NSStringFromSelector(sel));
    //1.判断没实现方法, 就动态添加方法
    if (sel == @selector(run)) {
        // 动态添加方法  IMP(函数指针)
        class_addMethod(self, sel, (IMP)runNew, "v@:");
        return YES;
    }
    // 2.动态添加的方法没有使用, 就给super
    return [super resolveInstanceMethod:sel];
}

2、- (id)forwardingTargetForSelector:(SEL)aSelector

在第一步返回NO时,会进入该方法,这是运行时给我们的第二次机会,用于指定哪个对象响应这个selector,不能指定为self。若返回nil,表示没有响应者,则会进入第三步。若返回某个对象,则会调用该对象的方法。

// 二. 消息转发重定向
- (id)forwardingTargetForSelector:(SEL)aSelector {
    NSLog(@"sel2 = %@", NSStringFromSelector(aSelector));
    return [[LKAnimation alloc] init];
}

3、- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector

若第二步返回nil,会进入该方法。这是运行时提供给我们的第三次补救机会。指定方法签名,若返回nil,则表示不处理。若返回方法签名,则会进入下一步。

// 三.生成方法签名
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    // 1. 转化成字符串
    NSString *sel = NSStringFromSelector(aSelector);
    // 2. 判断
    if([sel isEqualToString:@"run"]) {
    }
    return [NSMethodSignature signatureWithObjCTypes:"v@:"];
}

4、- (void)forwardInvocation:(NSInvocation *)anInvocation

若第三步返回方法签名后,则会调用该方法。我们通过anInvocation对象做很多处理,比如修改实现方法、修改响应对象等

// 四.方法的签名配发
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    NSLog(@"---%@--",anInvocation);
    // 1. 拿到消息
    SEL selector = [anInvocation selector];
    // 2. 转发消息
    Animation *anm = [[Animation alloc] init];
    if ([anm respondsToSelector:selector]) {
        // 调用这个对象, 去进行转发
        [anInvocation invokeWithTarget:anm];
    } else {
        [super forwardInvocation:anInvocation];
    }
}

5、- (void)doesNotRecognizeSelector:(SEL)aSelector

若没有实现- (void)forwardInvocation:(NSInvocation *)anInvocation方法,则会进入该方法。若我们没有实现该方法,则程序就会crash并抛出 unrecognized selector 异常。至此,动态解析的流程就结束了。

// 五.抛出异常
- (void)doesNotRecognizeSelector:(SEL)aSelector {
    NSString *selectorStr = NSStringFromSelector(aSelector);
    NSLog(@"这个--%@---方法不存在", selectorStr);
}

实例代码: https://pan.baidu.com/s/1ppd8WWEk59oPwI5gwZ--XQ

上一篇 下一篇

猜你喜欢

热点阅读