由plist文件加载的NSDictionary的内存泄漏

时间:2010-05-22 10:35:21

标签: objective-c iphone cocoa-touch memory-leaks nsdictionary

我有一个无法理解的内存泄漏问题!观看这个初始化方法:

- (id)initWithNomeCompositore:(NSString *)nomeCompositore nomeOpera:(NSString *)nomeOpera {

if (self = [super init]) {

    NSString *pathOpere = [[NSBundle mainBundle] pathForResource:kNomeFilePlistOpere ofType:kTipoFilePlist];
    NSDictionary *dicOpera = [NSDictionary dictionaryWithDictionary:
                                [[[NSDictionary dictionaryWithContentsOfFile:pathOpere]
                                objectForKey:nomeCompositore]
                                objectForKey:nomeOpera]];

    self.nomeCompleto = [[NSString alloc] initWithString:nomeOpera];
    self.compositore = [[NSString alloc] initWithString:nomeCompositore];
    self.tipologia = [[NSString alloc] initWithString:[dicOpera objectForKey:kKeyTipologia]];
}

return self;}

然后这个小变化(注意self.tipologia):

- (id)initWithNomeCompositore:(NSString *)nomeCompositore nomeOpera:(NSString *)nomeOpera {

if (self = [super init]) {

    NSString *pathOpere = [[NSBundle mainBundle] pathForResource:kNomeFilePlistOpere ofType:kTipoFilePlist];
    NSDictionary *dicOpera = [NSDictionary dictionaryWithDictionary:
                                [[[NSDictionary dictionaryWithContentsOfFile:pathOpere]
                                objectForKey:nomeCompositore]
                                objectForKey:nomeOpera]];

    self.nomeCompleto = [[NSString alloc] initWithString:nomeOpera];
    self.compositore = [[NSString alloc] initWithString:nomeCompositore];
    self.tipologia = [[NSString alloc] initWithString:@"Test"];
}

return self;}

在第一个变种中产生了内存泄漏,第二个不是!我只是不明白为什么!仪器证明了内存泄漏,强调了这一行:

[NSDictionary dictionaryWithContentsOfFile:pathOpere]

这是dealloc方法:

- (void)dealloc {
[tipologia release];
[compositore release];
[nomeCompleto release];
[super dealloc];}

2 个答案:

答案 0 :(得分:1)

请记住,alloc会返回您拥有的对象。

如果您将三个字符串属性声明为retain,则将这些对象分配给您的属性意味着您现在拥有每个属性两次,因为您已将其分配,并且因为您已将其分配给您的属性。对象仍然存活,因为没有任何东西释放他们的第二个所有权。

如果您将属性声明为copy(这是声明NSString属性的正确方法),则分配对象会将副本存储为属性的值。你没有对原始对象做任何进一步的处理,因为没有任何东西会释放它们。

无论哪种方式,这都是你的泄密。

该属性应声明为copy;如果已经存在,请不要尝试通过改变它来修复泄漏。

您不应在此处使用属性访问权限。请记住,分配给属性是set<PropertyName>:消息,并且您的对象尚未完全初始化。将消息发送到未完全初始化或未完全解除分配的对象会引发麻烦,特别是涉及子类时,因为它们可能会以超类不期望的方式覆盖访问器方法。

因此,仅在init中,直接分配给实例变量。仅在dealloc中,将release消息直接发送到实例变量中的对象。在其他地方,使用属性访问。

您也不应在此使用allocinitWithString:。它会起作用,但惯例是将copy消息发送到您已有的对象,与属性相同。将copy消息发送到输入字符串对象,然后将副本分配给实例变量。

当您使用属性访问时,请使用便捷构造函数(例如stringWith…:),因为这些返回您不拥有的对象。当您将这些对象分配给copy - 声明的属性时,您实际上将存储您拥有的副本。

另一种方法是使用allocinitWithWhatever:,然后立即autorelease该对象,然后再将其分配给该属性;这种方式会创建一个您拥有的对象,然后在将其分配给属性之前立即放弃所有权。

答案 1 :(得分:0)

尝试

nomeCompleto = [[NSString alloc] initWithString:nomeOpera];
compositore = [[NSString alloc] initWithString:nomeCompositore];
tipologia = [[NSString alloc] initWithString:[dicOpera objectForKey:kKeyTipologia]];

self.nomeCompleto = nomeOpera;
self.compositore = nomeCompositore;
self.tipologia = [dicOpera objectForKey:kKeyTipologia];

而不是self.xxx = [[yyy alloc] init...]


在原始代码中,赋值的RHS返回保留计数+1的对象,如果@property具有(retain)(copy),则最终保留计数将为是+2。因此,即使您在-dealloc中释放它们,净保留计数为+1,也会导致内存泄漏。


顺便说一句,没有必要调用+dictionaryWithDictionary:。只需使用

NSDictionary* dicOpera = [[[NSDictionary dictionaryWithContentsOfFile:pathOpere]
                            objectForKey:nomeCompositore]
                            objectForKey:nomeOpera];