在-init方法中直接设置ivars(不使用访问器)的EXC_BAD_ACCESS,为什么?

时间:2010-09-13 15:42:07

标签: iphone objective-c cocoa properties

我花了大约10个小时试图找到这个导致我的应用程序崩溃的错误,这是我看到的最后一个地方(好吧,它本来是,但最后一个地方,我曾预料到它)。

最初我以为我有内存管理问题(不平衡的保留/释放),因为每次我发送-removeAllObjects到填充了我的自定义对象的NSMutableArray时都会发生崩溃。第一次调用-removeAllObjects时不会发生崩溃。我可以清除一次数组,重新填充它,然后在第二次清除时,我会得到一个EXC_BAD_ACCESS。这是我的数组在第一个“循环”中填充了3个对象,而在第二个“循环”中再次填充了3个。当我在每个周期中只在阵列中存储1个对象时,需要4个周期才能崩溃(在-removeAllObjects的第4个调用中)。

我终于意识到,如果我更改了自定义对象的-init方法,崩溃就会消失。这是-init实现;所有4个ivars都是合成属性(nonatomic, retain),所有类型(NSString *)icon (NSNUmber *)

-(id)init {
    if (self = [super init]) {
        ip = @"";
        mac = @"";
        vendor = @"";
        icon = [NSNumber numberWithInt:0];
    }
    return self;
}

将其更改为此修复了错误:

-(id)init {
    if (self = [super init]) {
        self.ip = @"";
        self.mac = @"";
        self.vendor = @"";
        self.icon = [NSNumber numberWithInt:0];
    }
    return self;
}

我已经读过,不应该使用-init方法中的访问器,因为它会导致麻烦(例如,使用子类化)。

如果有人能够向我解释为什么当我使用访问器时我的bug会消失,我会非常感激!说真的让我疯了,因为这个,直到昨晚凌晨5点。

1 个答案:

答案 0 :(得分:9)

您正在直接分配但不保留实例变量。当您使用点语法时,您将触发合成属性的retain部分,从而保留它们。

-(id)init {
    if (self = [super init]) {
        ip = @"";
        mac = @"";
        vendor = @"";
        icon = [[NSNumber numberWithInt:0] retain];
    }
    return self;
}

这应该可能解决问题(但是,我有点惊讶,我认为10仍然在NSNumber的实例缓存中。也许不是。)。

从技术上讲,你也应该保留@“”字符串,但是你可以不这样做,因为这样的字符串是一个特殊的套接字常量字符串,来自编译后的可执行文件(作为NSString的私有子类,它不会覆盖到回应保留/释放/自动释放)。

memory management guide详细介绍了这一点。对于任何刚接触平台的人,我建议每月重读一次(不,真的 - 交错编码,偶尔重新阅读文档会经常发现你没有足够经验的微妙细节之前。我仍然每半年重读一次基本指南。)