iOS消息转发机制

NSObject的一些方法

+ (BOOL)resolveClassMethod:(SEL)sel ;
+ (BOOL)resolveInstanceMethod:(SEL)sel ;

这两个函数在运行时,没有找到SEL的IMP时就会执行,一个是类方法,一个是实例方法。这两个函数是给类利用class_addMethod添加函数的机会,如果实现了添加函数代码则返回YES,未实现返回NO。

- (id)forwardingTargetForSelector:(SEL)aSelector ;

这个方法是系统给了个将这个SEL转给其他对象的机会。 返回参数是一个对象,如果这个对象非nil、非self的话,系统会将运行的消息转发给这个对象执行。否则,继续查找其他流程

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector ;

这个函数和后面的forwardInvocation:是最后一个寻找IMP的机会。这个函数让重载方有机会抛出一个函数的签名,再由后面的forwardInvocation:去执行。

- (void)forwardInvocation:(NSInvocation *)anInvocation ;

在这个函数里可以将NSInvocation多次转发到多个对象中,这也是这种方式灵活的地方。(forwardingTargetForSelector只能以Selector的形式转向一个对象)

- (void)doesNotRecognizeSelector:(SEL)aSelector;

作为找不到函数实现的最后一步,NSObject实现这个函数只有一个功能,就是抛出异常。虽然理论上可以重载这个函数实现保证不抛出异常(不调用super实现),但是苹果文档着重提出“一定不能让这个函数就这么结束掉,必须抛出异常”。

方法转发流程

下面一张图详细的概括了每个函数调用的先后以及执行的前提

005COTsZzy7fFDAwP1B4f.jpg

Objective-C是一门动态语言,一个函数是由一个selector(SEL),和一个implement(IMP)组成的。Selector相当于地址,而Implement则是真实的地点(函数实现)。

和现实生活一样,地址可以随便写(@selector(XXX)),但是不一定都找得到这个地点,如果找不到系统会给程序几次机会来程序正常运行,实在没出路了才会抛出异常。

对应流程图就是:
在一个函数找不到时,Objective-C提供了三种方式去补救:
1、调用resolveInstanceMethod给个机会让类添加这个实现这个函数

2、调用forwardingTargetForSelector让别的对象去执行这个函数

3、调用methodSignatureForSelector(函数符号制造器)和forwardInvocation(函数执行器)灵活的将目标函数以其他形式执行。

4、如果都不中,调用doesNotRecognizeSelector抛出异常。

流程验证

定义一个类Runing

@interface Runing : NSObject

-(void)myClassA;

-(void)myClass;

@end

@implementation Runing

+ (BOOL)resolveInstanceMethod:(SEL)sel{
//运行时,可在此动态添加方法
    NSLog(@" %s",__func__);

        return NO;

}
- (id)forwardingTargetForSelector:(SEL)aSelector{
//在此可实现消息转发给他的对象 
NSLog(@" %s",__func__);

    return nil;

}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector{

           NSLog(@" %s",__func__);

     //return nil;       

   NSMethodSignature *methodSignature = [NSMethodSignature  signatureWithObjCTypes:"v@:@"];

   return methodSignature;   

}
- (void)forwardInvocation:(NSInvocation *)anInvocation{

        NSLog(@" %s",__func__); 

//若不调用super,找不到对应的方法实现则抛弃方法,结束流程!

     [super forwardInvocation:anInvocation];

    }

- (void)doesNotRecognizeSelector:(SEL)aSelector{

//若不调用super方法,则不会抛出异常

    NSLog(@" %s",__func__);

}
-(void)myClassA{

        NSLog(@"myClassA");

    }

@end

然后我们调用Runing的方法

Runing *runing = [[Runing alloc] init];

[runing performSelector:@selector(myClass)];

从上面我们可以看到myClass方法我们只是定义了,但是没有去实现。
流程分析:

  1. 因为没有找到myClass的实现,所以会执行resolveInstanceMethod方法,再次可以动态的添加方法的实现。若不添加则继续查找。

  2. 系统使用forwardingTargetForSelector方法,将myClass方法转给其返回值对象去执行。如果返回值是nilself则继续查找。

  3. methodSignatureForSelector方法会抛出一个函数签名,因为myClass方法没有实现,所以使用aSelector参数来抛出函数签名则会返回nil直接到流程5,所以这里用了一个固定值"v@:@",来返回一个NSMethodSignature类型。然后则会重复流程1查找IMP,找不到才会进入下一步

  4. forwardInvocation是最后一个寻找IMP的机会,它可以灵活的将目标函数以其他形式执行而不报错!如果调用super方法,则会继续执行。

  5. doesNotRecognizeSelector最后没有识别myClass方法,则抛弃并不抛出异常。如果实现该方法的super方法则会抛出异常!

你可能感兴趣的:(iOS消息转发机制)