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;}
在第一个变体中生成了内存泄漏,第二个则没有!我就是不明白为什么! 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
请记住,
alloc
返回一个您拥有的对象。如果您将三个字符串属性声明为
retain
,则将这些对象分配给您的属性意味着您现在拥有每个对象两次 - 一次是因为您分配了它,另一次是因为您将它分配给了您的属性。这些对象仍然存在,因为没有任何东西可以释放它们的第二所有权。如果将属性声明为
copy
(这是声明 NSString 属性的正确方法),则在此处分配对象会将副本存储为属性的值。您无需对原始对象进行任何进一步操作,这些对象仍然存在,因为没有任何东西可以释放它们。不管怎样,这都是你的泄漏。
该属性应声明为
copy
;如果已经存在,请不要尝试通过更改来修复泄漏。您不应在此处使用属性访问。请记住,分配给属性是一个
set:
消息,并且您的对象尚未完全初始化。向未完全初始化或未完全释放的对象发送消息会带来麻烦,特别是在涉及子类时,因为它们可能会以超类不希望的方式重写访问器方法。因此,仅在
init
中直接分配给实例变量。仅在dealloc
中,将release
消息直接发送到实例变量中的对象。在其他地方,请使用属性访问。您也不应该在这里使用
alloc
和initWithString:
。它会起作用,但约定是将复制消息发送到您已有的对象,就像属性一样。将copy
消息发送到您的输入字符串对象,然后将副本分配给您的实例变量。当您确实使用属性访问时,请使用便捷的构造函数(例如,
stringWith…:
),因为这些构造函数返回不属于您的对象。当您将这些对象分配给copy
声明的属性时,您实际上将存储您拥有的副本。另一种方法是使用
alloc
和initWithWhatever:
,然后立即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. Indealloc
only, sendrelease
messages directly to the objects in the instance variables. Everywhere else, use property accesses.You also should not use
alloc
andinitWithString:
here. It'll work, but the convention is to sendcopy
messages to the objects you already have, the same as the properties would do. Sendcopy
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 yourcopy
-declared properties, you will actually be storing copies that you do own.The other way would be to use
alloc
andinitWithWhatever:
, then immediatelyautorelease
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.尝试使用
or
代替
self.xxx = [[yyy alloc] init...]
。在原始代码中,赋值的 RHS 返回一个保留计数 +1 的对象,如果您使
@property
具有(retain)
或(copy )
,最终的保留计数将为+2。因此,即使在-dealloc
中释放这些,净保留计数也会+1,从而导致内存泄漏。顺便说一句,调用
+dictionaryWithDictionary:
是没有意义的。只需使用Try
or
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