objc_msgSend() 函数正确的使用方法

前天发了一个版本,从bugly监控发现频繁崩溃,崩溃类型是:SIGSEGV,由于忘记留’dSYM’文件,导致崩溃信息里面得到的信息很少(血的教训,发版一定要留’dSYM’文件!!),后来只能重新打了一个包,重现崩溃才找到bug,竟然是objc_msgSend()用错了,而且在这之前一直这么错着用!!!

objc_msgSend() 是OC runtime里面很重要的一个函数,用来发送消息,可以解决performSelector:方法最多只能传递两个参数的问题,所以runtime刚入门的人就喜欢用这个函数,但是没有真正理解其用法,比如我。

函数背后的原理和更深入的东西我就不说了,毕竟我也不会,只把函数的用法规则重新学习一下:

1
2
3
4
// 正常方法:
- (void)testMethod;
// 使用objc_msgSend()
objc_msgSend(self , @selector(testMethod));

但是以上写法是错误的,因为以上写法没有定义函数原型
正确的是以下写法:

1
((void(*) (id,SEL))objc_msgSend)(self, @selector(testMethod));

函数原型分解

所以,这个函数是要定义函数的返回值和参数类型的,例如要给一个Person对象发送消息,返回值为NSString:

1
2
3
4
5
6
7
8
9

// 正常方法
-(NSString *)getNameWithPerson:(Person *)person;
...
Person *aPerson = [[Person alloc] init];
NSString *name = [self getNameWithPerson:aPerson];

// 使用objc_msgSend()
NSString *name = ((NSString *(*) (id , SEL , Person *))objc_msgSend)(self , @selector(getNameWithPerson:) , aPerson);

带参数带返回值

备注:如果不定义函数原型,debug模式也许不会崩溃,但是打成Release包或者AdHoc包就有可能崩溃,所以还是要按规范来使用这个函数