恭喜你:你在Core Foundation中发现了一个bug!
正如Bill所怀疑的那样,这与Lion中的
标记指针有关。当你创建
NSNumber *d1 = [[NSNumber alloc] initWithInteger: 4];
d1
并不指向一个实际的NSNumber
实例。相反,d1
是一个包含0x4c3
的标记指针,其中0x4
是标记指针中的有效负载。
当你尝试使用标记指针作为弱属性的值时,Objective-C运行时执行的步骤之一是向实例发送-allowsWeakReference
以验证它是否可以用作弱引用。由于NSNumber
没有重写该方法,因此将执行NSObject
中的默认实现,该实现又会发送_isDeallocating
,然后调用_CFIsDeallocating()
,如下所示的堆栈跟踪:
#0 0x00007fff8ccdbacd in _CFIsDeallocating ()
#1 0x00007fff8ccd3119 in -[__NSCFNumber _isDeallocating] ()
#2 0x00007fff8be34b15 in -[NSObject(NSObject) allowsWeakReference] ()
#3 0x0000000100000ded in main () at test.m:12
如果您阅读
CFRuntime.c,您会发现
_CFIsDeallocating()
将相应的指针强制转换为
CFRuntimeBase *
以便读取
_cfinfo
。对于普通的Core Foundation对象,这是有效的,因为每个常规的Core Foundation引用都指向以
isa
指针开头的实例,后跟
_cfinfo
。然而,标记指针不指向实际(已分配的)内存,因此
_CFIsDeallocating()
试图解引用一个无效的指针,从而导致分段错误。
您应该
向苹果提交错误报告。同时,使用
strong
或
unsafe_unretained
属性。
编辑: 如果要获取回溯信息,请使用-g
选项构建您的可执行文件以包含调试信息,例如:
$ clang test.m -g -fobjc-arc -framework Foundation -o test
并使用GDB运行它:
$ gdb test
…
(gdb) run
程序将崩溃:
Program received signal EXC_BAD_ACCESS, Could not access memory.
Reason: KERN_INVALID_ADDRESS at address: 0x00000000000004cb
0x00007fff8ccdbacd in _CFIsDeallocating ()
在GDB中使用bt
命令获取回溯信息:
(gdb) bt
#0 0x00007fff8ccdbacd in _CFIsDeallocating ()
#1 0x00007fff8ccd3119 in -[__NSCFNumber _isDeallocating] ()
#2 0x00007fff8be34b15 in -[NSObject(NSObject) allowsWeakReference] ()
#3 0x00007fff875173a6 in weak_register_no_lock ()
#4 0x00007fff875179f9 in objc_storeWeak ()
#5 0x0000000100000c0e in -[Car setDoors:] (self=0x100113f60, _cmd=0x100000e7a, doors=0x4c3) at test.m:8
#6 0x0000000100000d45 in main () at test.m:23
然后使用quit
命令退出GDB:
(gdb) quit
在Xcode中,使用Mac OS X > 应用程序 > 命令行工具模板。运行程序时,Xcode应该自动在调试区域显示GDB提示。如果调试区域未显示在标准编辑器中,请选择“视图”>“调试区域”>“显示调试区域”。
编辑:这个 bug 在 OS X v10.7.3 中已经修复。