Objective-C - ARC - NSNumber - Segmentation Fault

11

我有一个 Objective-C 程序,正在使用 ARC (自动引用计数),它在第 23 行抛出了一个分段错误(参见下面的程序)。

问题 1)为什么会发生分段错误?

以下是程序:

#import<Foundation/Foundation.h>

@interface Car : NSObject
@property (weak) NSNumber* doors;
@end

@implementation Car 
@synthesize doors;
@end

int main()
{
    system("clear");

    @autoreleasepool
    {    
        Car *car1 = [[Car alloc] init];

        printf("1\n");
        NSNumber *d1 = [[NSNumber alloc] initWithInteger: 4]; 

        printf("2\n");
        car1.doors = d1;   //Segmentation fault.. why ?

        printf("3\n");
    }   

    printf("---- end\n");

    return(0);
}

输出:

1
2
Segmentation fault: 11

这个错误是KERN_INVALID_ADDRESS,看起来更像是总线错误而不是分段错误。 - Daniel
1
只是好奇,你使用printf()而不是NSLog()的原因是什么? - 5StringRyan
我刚接触Objective-C,所以我倾向于使用printf。 - user1046037
2个回答

24
恭喜你:你在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()试图解引用一个无效的指针,从而导致分段错误。
您应该向苹果提交错误报告。同时,使用strongunsafe_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 中已经修复。


非常感谢大家,这是非常有帮助和详细的解释。 - user1046037
2
不错的调试工作...感谢您的追踪,是的,请务必提交一个错误报告。我怀疑这是一个重复的问题,但是...以防万一... - bbum
如何打印追踪信息(clang-ARC)?我是从命令行使用它的。我想知道如何在命令行上打印追踪信息,以及当我使用Xcode时应该怎么做? - user1046037
我之前提供的苹果链接无法登录,以便报告错误。 - user1046037
您需要一个已注册的开发者账户(免费或付费均可)。但是最近几天,错误报告网站有些不稳定,请继续尝试! - user557219
刚刚也遇到了这个问题,真希望在来到这里之前就在这里搜索了!非常感谢你的出色回答。+1 - mattjgalloway

0

请提供回溯信息。另外,您使用的是哪个平台?

如果目标是64位OS X,则可能与NSNumber和标记指针相关的错误。


我刚刚尝试了一下,它在_CFIsDeallocating()内部崩溃了。我认为这看起来像是带标签指针的一个bug。 - Lily Ballard

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接