_objc_msgForward的作用,直接调用会发生什么

demo地址

_objc_msgForward是IMP类型的,用于消息转发的,当像一个对象发送消息,但他没有实现的时候,_objc_msgForward会尝试做消息转发。

objc_msgSend的动作比较清晰,在“消息传递”过程中,:首先在 Class 中的缓存查找 IMP (没缓存则初始化缓存),如果没找到,则向父类的 Class 查找。如果一直查找到根类仍旧没有实现,则用_objc_msgForward函数指针代替 IMP 。最后,执行这个 IMP 。

_objc_msgForward消息转发需要做的几件事:

1. 调用+ (BOOL)resolveInstanceMethod:(SEL)sel(或 + (BOOL)resolveClassMethod:(SEL)sel)方法,在此方法中添加相应selector以及IMP即可,允许用户在此时为该Class动态添加实现。如果有实现了,则调用并返回YES,那么重新开始objc_msgSend流程。对象会相应这个选择器,一般是因为它已经调用过class_addMethod。如果仍没实现,继续下面的步骤
2. 调用- (id)forwardingTargetForSelector:(SEL)aSelector方法,尝试找到一个能相应该消息的对象。如果获取到,则直接把消息转发给它,返回非nil对象。否则返回 nil ,继续下面的动作。
3. 调用- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector方法,尝试获得一个方法签名。如果能获取,则返回非nil:创建一个 NSlnvocation 并传给forwardInvocation:
调用- (void)forwardInvocation:(NSInvocation *)anInvocation方法,将获取到的方法签名包装成 Invocation 传入,如何处理就在这里面了,并返回非nil。如果获取不到,则直接调用4抛出异常。
4. 调用- (void)doesNotRecognizeSelector:(SEL)aSelector,默认的实现是抛出异常。如果第3步没能获得一个方法签名,执行该步骤。

上面前4个方法均是模板方法,开发者可以override,由 runtime 来调用。最常见的实现消息转发:就是重写步骤3的两个方法,吞掉一个消息或者代理给其他对象都是没问题的

也就是说_objc_msgForward在进行消息转发的过程中会涉及以下这几个方法:

    • (BOOL)resolveInstanceMethod:(SEL)sel方法 (或 + (BOOL)resolveClassMethod:(SEL)sel)。
    • (id)forwardingTargetForSelector:(SEL)aSelector方法
    • (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector方法
    • (void)forwardInvocation:(NSInvocation *)anInvocation方法
    • (void)doesNotRecognizeSelector:(SEL)aSelector 方法

一共有三次机会。假设有A类和B类,分别对应a对象,b对象,a执行了一个不存在的方法

[self performSelector:@selector(sel:) withObject:@"haha"];

第一次机会,在+ (BOOL)resolveInstanceMethod:(SEL)sel方法中可以通过class_addMethod(self.class, sel, (IMP)dynamicMethodIMP, "@@:"); 添加动态方法

id dynamicMethodIMP(id self, SEL _cmd, NSString *str)
{
    NSLog(@"%s:动态添加的方法",__FUNCTION__);
    NSLog(@"%@", str);
    return @"1";
}

添加成功后不会再继续执行
第二次机会,- (id)forwardingTargetForSelector:(SEL)aSelector 系统给了个将这个SEL转给其他对象的机会,返回b对象,b会重新走一次本过程。

注意,这里不要返回 self,会形成死循环。

第三次机会,这个函数和后面的forwardInvocation:是最后一个寻找IML的机会。这个函数让重载方有机会抛出一个函数的签名,再由后面的forwardInvocation:去执行。
doesNotRecognizeSelector作为找不到函数实现的最后一步,NSObject实现这个函数只有一个功能,就是抛出异常。

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

总结一下,在一个函数找不到时,Objective-C提供了三种方式去补救:

1、调用resolveInstanceMethod给个机会让类添加这个实现这个函数

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

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

如果都不行,调用doesNotRecognizeSelector抛出异常。

直接调用_objc_msgForward很危险,如果用不好会直接导致程序Crash。

_objc_msgForward隶属 C 语言,有三个参数
_objc_msgForward是 IMP 类型,用于消息转发的:当向一个对象发送一条消息,但它并没有实现的时候,_objc_msgForward会尝试做消息转发。一旦调用_objc_msgForward,将跳过查找 IMP 的过程,直接触发“消息转发”。

你可能感兴趣的:(_objc_msgForward的作用,直接调用会发生什么)