【问题标题】:Memory leak for NSDictionary loaded by plist fileplist 文件加载的 NSDictionary 的内存泄漏
【发布时间】:2011-02-22 16:24:21
【问题描述】:

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

- (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;}

在第一个变种中是产生了内存泄漏,第二个不是!我就是不明白为什么!内存泄漏由 Instruments 证明,突出显示以下行:

[NSDictionary dictionaryWithContentsOfFile:pathOpere]

这是dealloc方法:

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

【问题讨论】:

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


    【解决方案1】:

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

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

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

    不管怎样,那是你的泄漏。

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

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

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

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

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

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

    【讨论】:

    • 感谢 Peter Hosey,现在我了解了该机制的功能。真的谢谢!
    【解决方案2】:

    试试

    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];
    

    【讨论】:

      猜你喜欢
      • 2011-11-08
      • 2011-09-11
      • 2011-05-20
      • 2011-04-02
      • 1970-01-01
      • 2012-05-22
      • 1970-01-01
      • 2011-06-10
      • 1970-01-01
      相关资源
      最近更新 更多