2010-05-22 20 views
2

我有一个内存泄漏问题,只是无法理解!关注此初始化方法:由plist文件加载的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];} 

回答

1

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

如果您将三个字符串属性声明为retain,那么将这些对象分配给您的属性意味着您现在每拥有一个对象两次 - 一次是因为您已将其锁定,并且又因为已将其分配给您的属性。由于没有释放第二个所有权,对象保持活着。

如果您声明属性为copy(这是声明NSString属性的正确方法),那么在该对象中分配对象会将副本存储为属性的值。您对原始对象没有做任何进一步的处理,因为没有释放它们,所以它们仍然活着

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

该物业应宣布为copy;如果已经存在,不要试图通过改变漏洞来修复泄漏。

你不应该在这里使用财产访问。请记住,分配属性是一个set<PropertyName>:消息,并且您的对象尚未完全初始化。将消息发送给未完全初始化或未完全释放的对象会造成麻烦,特别是在涉及子类时,因为它们可能会以超类不期望的方式覆盖访问器方法。

所以,只在init中,直接赋值给实例变量。仅在dealloc中,将release消息直接发送到实例变量中的对象。在其他地方,使用属性访问。

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

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

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

+0

感谢Peter Hosey,现在我明白了这个机制的功能。真的感谢! – Pask 2010-05-22 10:49:00

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的对象,如果你让有(retain)(copy),该@property最终保留计数会+2。因此,即使您在-dealloc中释放这些内容,净保留计数为+1,也会导致内存泄漏。


顺便说一下,没有要求拨打+dictionaryWithDictionary:。只需使用

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

一切正常,谢谢KennyTM! – Pask 2010-05-22 10:45:15