前言
最近跟同事讨论了有关methodSignatureForSelector:的问题,大概如下:
- 一个ProtocolA声明了一个实例方法funcA,然后一个类ClassA声明实现这个ProtocolA,但是并没有实现方法funcA,对ClassA的实例调用
methodSignatureForSelector:@selector(funcA)能否返回正确的signature?
- 一个类ClassB,在@interface声明了实例方法funcB,但是没有实现funcB,对ClassB的实例调用
methodSignatureForSelector:@selector(funcB),能否返回正确的signature?
写个Demo验证了下,结果非常有意思,不由得好奇起来,所以深入研究了下methodSignatureForSelector:的实现,然后自己模拟实现出来。
Github地址:TTGRemakeMethodSignatureForSelector
详细的研究过程如下:
写Demo验证问题
测试类TestClass和Protocol如下:
1 2 3 4 5 6 7 8 9
| @protocol TestProtocol <NSObject> - (void)protocolTestFunc1; @end
@interface TestClass : NSObject <TestProtocol> - (void)testFunc1; @end @implementation TestClass @end
|
调用methodSignatureForSelector:获取protocolTestFunc1和testFunc1的methodSignature,protocolTestFunc1可以获取到,但是testFunc1就不行。
扩展到所有情况
分析整理下可能的情况:
对于一个类:
- 方法没有声明,也没有实现
- 方法有声明,但是没有实现
- 方法没有声明,但是有实现
- 实例方法和类方法都有上面1、2、3的情况
对于一个Protocol:
- 方法有声明,但是没有实现
- 实例方法和类方法
- required和optional方法
经过验证,发现:
- 对于类,只要@implementation里实现了方法,不管@interface是否有声明,都可以返回methodSignature
- 对于Protocol声明的方法,不管实例方法还是类方法,不管required还是optional的,都可以返回methodSignature
所以,有必要进一步研究methodSignatureForSelector:的实现。
从Runtime源码入手
methodSignatureForSelector:是NSObject的方法,所以想知道原理,第一步当然就是翻看Runtime的源码,看看有没有实现,最新的objc4-709源码,实现如下:
1 2 3 4 5 6
|
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { _objc_fatal("-[NSObject methodSignatureForSelector:] " "not available without CoreFoundation"); }
|
可见,Runtime源码是没有实现的,要到CoreFoundation里面找。
找CoreFoundation源码
接着Google CoreFoundation源码,找到https://opensource.apple.com/source/CF/CF-1153.18/,怎么都找不到有关methodSignatureForSelector:的实现,难道要就此打住?
反编译大法
既然找不到源码,那就只好祭出反编译大法,直接上汇编=。=
找到CoreFoundation动态库
CoreFoundation是在程序运行时链接上去的动态库,属于系统动态库的一部分,所以首先要找到CoreFoundation动态库的二进制代码文件,命令行:
1 2
| sudo -i find / -name CoreFoundation.framework
|
搜到的结果很多,以iPhone模拟器的为例:
1
| /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/System/Library/Frameworks/CoreFoundation.framework
|
打开CoreFoundation.framework文件夹,CoreFoundation文件就是要反编译的文件。
Hopper Disassembler反编译
用Hopper Disassembler打开CoreFoundation文件,在Labels搜索methodSignatureForSelector:,就能找到对应的汇编实现代码,如下:

Hopper翻译的代码如下:
1 2 3 4 5 6 7 8 9 10 11
| void * +[NSObject methodSignatureForSelector:](void * self, void * _cmd, void * arg2) { rdi = self; rbx = arg2; if ((rbx != 0x0) && (___methodDescriptionForSelector(object_getClass(rdi), rbx) != 0x0)) { rax = [NSMethodSignature signatureWithObjCTypes:rdx]; } else { rax = 0x0; } return rax; }
|
可发现,关键在于___methodDescriptionForSelector:方法。
___methodDescriptionForSelector:方法的实现
进一步看___methodDescriptionForSelector:的实现。
汇编代码、Hopper翻译的代码都很长,循环也不见了,全是goto:

经过反复校对整理(排除Hopper的错误=。=),可以整理出主要的流程如下图所示:

根据流程,可以得出,___methodDescriptionForSelector:方法:
- 用
class_copyProtocolList、protocol_getMethodDescription方法,检查是否有对应的selector,不管是否实现
- 用
class_getInstanceMethod检查selector是否有implementation,注意,是implementation,跟是否声明了没有关系
所以,符合前面的结论。
模拟实现
流程都有了,不如更进一步,自己实现一个methodSignatureForSelector:方法出来~
模拟实现___methodDescriptionForSelector:
按照流程,实现代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| struct objc_method_description ttg_MethodDescription(Class class, SEL sel) { struct objc_method_description description = (struct objc_method_description){NULL, NULL}; Class currentClass = class; while (currentClass && description.name == NULL) { unsigned int count = 0; __unsafe_unretained Protocol **protocols = class_copyProtocolList(currentClass, &count); for (unsigned int i = 0; i < count; i++) { description = protocol_getMethodDescription(protocols[i], sel, YES, class_isMetaClass(currentClass) ^ 1); if (description.name != NULL) { break; } description = protocol_getMethodDescription(protocols[i], sel, NO, class_isMetaClass(currentClass) ^ 1); if (description.name != NULL) { break; } } free(protocols); if (description.name != NULL) { return description; } currentClass = class_getSuperclass(currentClass); } Method method = class_getInstanceMethod(class, sel); if (method) { return *method_getDescription(method); } else { return (struct objc_method_description){NULL, NULL}; } }
|
- 用description记录迭代中的结果,只要找到就返回
- 用currentClass记录每次迭代的类class
- Protocol的@required和@optional方法都要检查
- 注意释放Protocol数组
- 最后才用
class_getInstanceMethod获取类本身的实现
模拟实现methodSignatureForSelector:
用Category给NSObject加两个方法:
1 2 3 4
| @interface NSObject (TTGRemakeMethodSignatureForSelector) - (NSMethodSignature *)ttg_methodSignatureForSelector:(SEL)sel; + (NSMethodSignature *)ttg_methodSignatureForSelector:(SEL)sel; @end
|
实现基本一样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| @implementation NSObject (TTGRemakeMethodSignatureForSelector)
- (NSMethodSignature *)ttg_methodSignatureForSelector:(SEL)sel { struct objc_method_description description = ttg_MethodDescription([self class], sel); if (sel && description.types != NULL) { return [NSMethodSignature signatureWithObjCTypes:description.types]; } else { return nil; } }
+ (NSMethodSignature *)ttg_methodSignatureForSelector:(SEL)sel { struct objc_method_description description = ttg_MethodDescription(object_getClass(self), sel); if (sel && description.types != NULL) { return [NSMethodSignature signatureWithObjCTypes:description.types]; } else { return nil; } }
@end
|
注意要获取正确的”class”:
- 对于对象实例,从
[obj class]、object_getClass(obj)结果一致,都是对象的类
- 对于类,要获取元类,也就是metaClass,只能用
object_getClass(class)
验证模拟实现
验证自己模拟实现的methodSignatureForSelector:和系统的是否一致:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| NSMethodSignature *signature1 = [test methodSignatureForSelector:@selector(testFunc1)]; NSMethodSignature *signature2 = [test ttg_methodSignatureForSelector:@selector(testFunc1)]; NSLog(@"signature1 == signature2: %d", [signature1 isEqual:signature2]);
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[test ttg_methodSignatureForSelector:@selector(testFunc1)]]; invocation.target = test; invocation.selector = @selector(testFunc1); [invocation invoke];
invocation = [NSInvocation invocationWithMethodSignature:[TestClass ttg_methodSignatureForSelector:@selector(staticTestFunc1)]]; invocation.target = [TestClass class]; invocation.selector = @selector(staticTestFunc1); [invocation invoke];
|
结果跟系统实现的行为一致,更多Case见Github的Demo。
总结
从两个小问题出发,到最后通过反编译、模拟实现,终于搞清楚了methodSignatureForSelector:的原理、流程、各种隐藏特性!哈哈~~过瘾~~
上一篇:转-阿里巴巴国际无线技术部 - 在这里遇见最好的自己
下一篇:反编译分析Xcode8的Bug, release下连续两次调用有二级指针参数的空方法会Crash