前言
最近跟同事讨论了有关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如下:
@protocol TestProtocol <NSObject>
- (void)protocolTestFunc1;
@end
@interface TestClass : NSObject <TestProtocol> // 不实现Protocol任何方法
- (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源码,实现如下:
// From objc4-709 NSObject.mm
// Replaced by CF (returns an NSMethodSignature)
- (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动态库的二进制代码文件,命令行:
sudo -i
find / -name CoreFoundation.framework
搜到的结果很多,以iPhone模拟器的为例:
/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翻译的代码如下:
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:
按照流程,实现代码如下:
// 模拟实现___methodDescriptionForSelector方法:
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) {
// 获取Protocol列表
unsigned int count = 0;
__unsafe_unretained Protocol **protocols = class_copyProtocolList(currentClass, &count);
// 遍历所有Protocol
for (unsigned int i = 0; i < count; i++) {
// Required 方法
description = protocol_getMethodDescription(protocols[i], sel, YES, class_isMetaClass(currentClass) ^ 1);
if (description.name != NULL) {
break;
}
// Optional 方法
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加两个方法:
@interface NSObject (TTGRemakeMethodSignatureForSelector)
- (NSMethodSignature *)ttg_methodSignatureForSelector:(SEL)sel;
+ (NSMethodSignature *)ttg_methodSignatureForSelector:(SEL)sel;
@end
实现基本一样:
@implementation NSObject (TTGRemakeMethodSignatureForSelector)
- (NSMethodSignature *)ttg_methodSignatureForSelector:(SEL)sel {
struct objc_method_description description = ttg_MethodDescription([self class], sel); // 注意!是获取实例的class
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); // 必须用object_getClass获取metaClass
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:和系统的是否一致:
// 测试equal相等
NSMethodSignature *signature1 = [test methodSignatureForSelector:@selector(testFunc1)];
NSMethodSignature *signature2 = [test ttg_methodSignatureForSelector:@selector(testFunc1)];
NSLog(@"signature1 == signature2: %d", [signature1 isEqual:signature2]);
// Invoke实例方法
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:[test ttg_methodSignatureForSelector:@selector(testFunc1)]];
invocation.target = test;
invocation.selector = @selector(testFunc1);
[invocation invoke];
// Invoke类方法
invocation = [NSInvocation invocationWithMethodSignature:[TestClass ttg_methodSignatureForSelector:@selector(staticTestFunc1)]];
invocation.target = [TestClass class];
invocation.selector = @selector(staticTestFunc1);
[invocation invoke];
结果跟系统实现的行为一致,更多Case见Github的Demo。
总结
从两个小问题出发,到最后通过反编译、模拟实现,终于搞清楚了methodSignatureForSelector:的原理、流程、各种隐藏特性!哈哈~~过瘾~~