ios运行时消息转发机制

1消息转发步骤

如果我们在 Objective C 中向一个对象发送它无法处理的消息,会出现什么情况呢?ios发送消息是通过 objc_send(id, SEL, …) 来实现的,它会首先在对象的类对象的 cache,method list 以及父类对象的 cache, method list 中依次查找 SEL 对应的 IMP;如果没有找到且实现了动态方法决议机制就会进行决议,如果没有实现动态方法决议机制或决议失败且实现了消息转发机制就会进入消息转发流程,否则程序 crash。也就是说如果同时提供了动态方法决议和消息转发,那么动态方法决议先于消息转发,只有当动态方法决议依然无法正确决议 selector 的实现,才会尝试进行消息转发。 动态方法决议和消息转发主要涉及以下方法:

+(BOOL)resolveClassMethod:(SEL)sel;
+(BOOL) resolveInstanceMethod:(SEL)sel;
-(id)forwardingTargetForSelector:(SEL)aSelector;
-(void)forwardInvocation:(NSInvocation *)invocation;

首先系统会先调用+(BOOL)resolveClassMethod:(SEL)sel;或者+(BOOL) resolveInstanceMethod:(SEL)sel;方法(前者为调用类方法时调用,后者为调用实例方法时调用),通过runtime,该方法可以实现动态添加方法,实现如下:处理方法在dynamicMethodIMP中处理掉

+(BOOL) resolveInstanceMethod:(SEL)sel{
    NSLog(@"%s",__func__);
    class_addMethod([self class], sel, (IMP)dynamicMethodIMP, "v@:"); 
    return YES;
}
void dynamicMethodIMP(id self, SEL _cmd) {
    NSLog(@" >> dynamicMethodIMP ");
    //    NSLog(@"%s %@",__func__,self);
}

如果当前类中没有实现改方法则进行消息转发,转发有两个方式

-(id)forwardingTargetForSelector:(SEL)aSelector;
-(void)forwardInvocation:(NSInvocation *)invocation;

这两个方法都可以将调用转发到另一个实例上进行处理,系统有限调用forwardingTargetForSelector方法
两者的区别forwardingTargetForSelector处理比较简单的消息转发,官方文档如下:

This method gives an object a chance to redirect an unknown message sent to it before the much more expensive forwardInvocation: machinery takes over. This is useful when you simply want to redirect messages to another object and can be an order of magnitude faster than regular forwarding. It is not useful where the goal of the forwarding is to capture the NSInvocation, or manipulate the arguments or return value during the forwarding.

forwardInvocation处理比较复杂,官方文档如下

Implementations of the forwardInvocation: method can do more than just forward messages. forwardInvocation: can, for example, be used to consolidate code that responds to a variety of different messages, thus avoiding the necessity of having to write a separate method for each selector. A forwardInvocation: method might also involve several other objects in the response to a given message, rather than forward it to just one. NSObject’s implementation of forwardInvocation: simply invokes the doesNotRecognizeSelector: method; it doesn’t forward any messages. Thus, if you choose not to implement forwardInvocation:, sending unrecognized messages to objects will raise exceptions.

forwardInvocation在使用的收需要先实现methodSignatureForSelector:方法

To respond to methods that your object does not itself recognize, you must override methodSignatureForSelector: in addition to forwardInvocation:. The mechanism for forwarding messages uses information obtained from methodSignatureForSelector: to create the NSInvocation object to be forwarded. Your overriding method must provide an appropriate method signature for the given selector, either by pre formulating one or by asking another object for one.

-(void)forwardInvocation:(NSInvocation *)invocation
{
    NSLog(@"%s ",__func__);
    [invocation invokeWithTarget:[NSClassFromString(@"Test2") new]];                                                                      
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if(aSelector == @selector(ABC))
    {
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];
    }
    return nil;
}

猜你喜欢

转载自blog.csdn.net/chenjin360/article/details/81630163