ios学习之彻底搞清楚消息转发

首先要搞清楚一点的是,在OC中方法的调用是向对象发送消息。

比如:[person    run],就是向person这个对象发送叫做run的消息。因为Objective-C运行时的存在,即使person对应的类中没有run这个方法,在编译期间也不会报错,但程序最终会报错:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[Person run]: unrecognized selector sent to instance

本文就从这个异常说起,彻底搞清楚Objective-C的消息转发机制。

当向一个对象发送某个消息时,程序在运行期间会通过对象所属类的方法列表中去找对应的方法,找到就执行,找不到就从父类中找。如果最后在NSObject中也没有这个方法时,运行时系统会提供3套备选方案进行补救。

第一种方案:

+ (BOOL)resolveInstanceMethod:(SEL)sel

+ (BOOL)resolveClassMethod:(SEL)sel

如果是实例方法,则需要实现resolveInstanceMethod:(SEL)sel方法,如果是类方法,实现resolveClassMethod:(SEL)sel即可。

比如上面Person类中的实例方法run,如果没有实现这个方法,就可以通过实现resolveInstanceMethod:(SEL)sel来进行补救。

在Person.m文件中添加方法:

+ (BOOL)resolveInstanceMethod:(SEL)sel{

            if(sel==@selector(run)){

                    class_addMethod(self,sel,(IMP)runMethod,"v@:");

                    return YES;

            }

            return [super resolveInstanceMethod :sel];

}

void  runMethod(id self,SEL _cmd){

            NSLog(@"重新实现run方法");

}

一句话总结这种方案就是:给目标对象动态添加一个方法实现。

第二种方案:

实现forwardingTargetForSelector方法。

还是上面的例子,新建一个类Tiger,在这个类中实现方法run如下:

-(void) run{

    NSLog("tiger run ");

}

在Person.m文件中添加方法:

-(id)forwardingTargetForSelector:(SEL)sel{

            return [ [Tiger alloc]init];

}

一句话总结这种方案,本身类没有实现的方法,可以转给其他类实现。

第三种方案:

实现methodSignatureForSelector和forwardInvocation两个方法,第一个方法用于生成签名,第二个方法实际作为转发对象调用方法。

-(void)forwardInvocation:(NSInvocation*)invocation{

        SEL selector=[invocation selector];

        Tiger *tiger=[[Tiger alloc]init];

         if(tiger respondsToSelector:selector){

                [invocation  invokeWithTarget:tiger];

        }

}

这种方案本质上和第二种方案一样,转发给其他类进行实现。

你可能感兴趣的:(ios学习之彻底搞清楚消息转发)