plist 文件加载的 NSDictionary 内存泄漏

发布于 2024-09-02 17:10:44 字数 1921 浏览 4 评论 0原文

我有一个内存泄漏问题,只是无法理解!观察这个初始化方法:

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

I have a memory leak problem that just can not understand! Watch this initialization method:

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

Then this little variation (note 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;}

In the first variant is generated a memory leak, the second is not! And I just can not understand why! The memory leak is evidenced by Instruments, highlighted the line:

[NSDictionary dictionaryWithContentsOfFile:pathOpere]

This is the dealloc method:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

冰火雁神 2024-09-09 17:10:44

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

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

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

不管怎样,这都是你的泄漏。

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

您不应在此处使用属性访问。请记住,分配给属性是一个 set: 消息,并且您的对象尚未完全初始化。向未完全初始化或未完全释放的对象发送消息会带来麻烦,特别是在涉及子类时,因为它们可能会以超类不希望的方式重写访问器方法。

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

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

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

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

Remember that alloc returns an object that you own.

If you declared your three string properties as retain, assigning those objects to your properties means you now own each one twice—once because you allocked it, and again because you assigned it to your property. The objects remain alive because nothing releases their second ownerships.

If you declared the properties as copy (which is the correct way to declare an NSString property), assigning the object there stores a copy as the value of the property. You do nothing further with the original objects, which remain alive because nothing releases them.

Either way, that is your leak.

The property should be declared as copy; if it already is, don't try to fix the leak by changing that.

You should not use property access here. Remember that assigning to a property is a set<PropertyName>: message, and that your object is not fully initialized yet. Sending a message to an incompletely-initialized or incompletely-deallocated object is asking for trouble, particularly when subclasses are involved, since they may override the accessor methods in ways the superclass doesn't expect.

So, in init only, assign directly to the instance variables. In dealloc only, send release messages directly to the objects in the instance variables. Everywhere else, use property accesses.

You also should not use alloc and initWithString: here. It'll work, but the convention is to send copy messages to the objects you already have, the same as the properties would do. Send copy messages to your input string objects, then assign the copies to your instance variables.

When you do use property accesses, use the convenience constructors (stringWith…:, for example), as these return objects that you do not own. When you assign these objects to your copy-declared properties, you will actually be storing copies that you do own.

The other way would be to use alloc and initWithWhatever:, then immediately autorelease that object before assigning it to the property; this way creates an object that you own, then immediately gives up ownership before assigning it to the property.

ぃ弥猫深巷。 2024-09-09 17:10:44

尝试使用

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

or

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

Try

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

or

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

instead of self.xxx = [[yyy alloc] init...].


In the original code, the RHS of the assignment returns an object of retain count +1, and if you make the @property having (retain) or (copy), the final retain count would be +2. Therefore, even if you release these in -dealloc, the net retain count is +1, causing a memory leak.


BTW, there's no point calling +dictionaryWithDictionary:. Just use

NSDictionary* dicOpera = [[[NSDictionary dictionaryWithContentsOfFile:pathOpere]
                            objectForKey:nomeCompositore]
                            objectForKey:nomeOpera];
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文