OC的message forwarding
众所周知,OC是一种基于消息的语言,这种特质带给这种语言很强的动态性。当运行时调用一个类或对象的某个功能时,在OC中是以“消息”的方式发送给一个实体(类或对象)。message
发送给一个实体后,运行时会安装实体的内存模型去查找这个消息,但如果这个实体没有这个消息的处理函数是不是意味着结束呢?不,运行时还有一个称为message forwarding
查询机制。
啥都不说了🙊,上代码
1 | #include <Foundation/Foundation.h> |
运行输出如下:
1 | 2017-01-04 11:44:25.743 unitTest.m_sublime_build[6876:645642] resolveInstanceMethod: didnotexistmethod: |
what? resolveInstanceMethod
被调用了两次,forwardInvocation
没有被调用。额,原来是forwardInvocation
被调用必须是methodSignatureForSelector
方法可以返回其信息。那么methodSignatureForSelector
有什么作用呢?我们看一下methodSignatureForSelector
的原型,传入一个SEL
返回NSMethodSignature
1 | - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector |
看一下所谓方法签名到底是什么NSMethodSignature
,难道是某种算法产生的摘要?不,如下有三个方法:
1 | - (void)method1Test |
NSMethodSignature
的定义如下,可以从类方法signatureWithObjCTypes
看出,这里传入字符串types
就是所谓的签名:
1 | @interface NSMethodSignature : NSObject { |
写一个简单的函数可以从一个NSMethodSignature
中还原出type
1 | void whatIsMethodSignature(NSMethodSignature *methodSign) |
最后得到上面三个函数的签名如下:
1 | 2017-01-04 15:10:00.099 unitTest.m_sublime_build[7332:729268] NSMethodSignature: v@: |
对比上面三个函数的定义我们很快就能总结出 v=void @=(NObject类,当然包括self) :=_cmd q=NSInteger。没错就是这么变态。进而我们总结出函数的签名可以告诉编译器一个函数的结构信息,返回值类型,参数个数与类型。有了签名runtime
才能构建出anInvocation
传入给forwardInvocation
。
1 | SEL selector = @selector(method1Test); |
1 | #include <Foundation/Foundation.h> |
输出如下信息,完整的message forwarding 流程就走了一遍。
1 | 2017-01-04 15:39:12.808 unitTest.m_sublime_build[7432:753061] resolveInstanceMethod: didnotexistmethod |
总结一下:
- resolveInstanceMethod函数在运行时(runtime),没有找到SEL的IML时就会执行。这个函数是给类利用class_addMethod添加函数的机会。如果实现了添加函数代码则返回YES,未实现返回NO。
- forwardingTargetForSelector:系统给了个将这个SEL转给其他对象的机会。返回参数是一个对象,如果这个对象非nil、非self的话,系统会将运行的消息转发给这个对象执行。
- methodSignatureForSelector:这个函数和后面的forwardInvocation:是最后一个寻找IML的机会。这个函数让重载方有机会抛出一个函数的签名,再由后面的forwardInvocation:去执行。
- 真正执行从methodSignatureForSelector:返回的NSMethodSignature。在这个函数里可以将NSInvocation多次转发到多个对象中,这也是这种方式灵活的地方。