最近程序在调用 NSInvokation
来获取函数返回值的时候,如果返回值是对象的话就会引起 EXC_BAD_ACCESS
崩溃。
对付 EXC_BAD_ACCESS
问题一般需要在 Edit Scheme... -> Diagnostics
中开启 Zombie Objects
选项,于是在崩溃时调实窗口打印出来了:
1 | *** -[CFString release]: message sent to deallocated instance 0x2813f0200 |
是反射出来的返回值被再次释放了。
获取返回值的代码如下:
1 | id retval; |
这里的 retval
会被释放两次,导致崩溃。
原因及解决方案
网上的解释是在 ARC 下才会导致这种错误。getReturnValue
这个方法不会对内存进行管理,id retval
这句会被认为 retval
是 __strong
类型的,所以在离开这个范围内会调用一次 release
,结果就导致了再次释放而崩溃。
所以需要在这里声明一下 retval
不是 __strong
类型的,也就是说这里不持有这个对象,离开范围也不需要对他进行释放。有几种解决方案:
1)
1 | id __unsafe_unretained retVal; // 在这里声明不持有retVal,使用 __weak 或 __autoreleasing 都可以 |
2)
1 | void *tmpVal; |
更推荐第二种写法,添加了从 void*
到 id
的桥接,更加严谨一些。
其他
- 在调试过程中,发现如果返回的字符串为
__NSCFConstantString
或者NSTaggedPointerString
时,并不会崩溃,这又涉及到了类簇的概念。
参考链接
- NSInvocation returns value but makes app crash with EXC_BAD_ACCESS, stackoverflow.com.
- getReturnValue, developer.apple.com.
- iOS开发-NSString类簇探究, 靠近星星的太阳。
- Objective-C: difference between id and void *, stackoverflow.com.