iPhone:这是不是泄漏

时间:2010-02-12 15:27:28

标签: iphone memory-management memory-leaks

最近有人在Stack Overflow上告诉我,下面的代码没有泄漏,该属性处理保留本身:

self.locationManager = [[CLLocationManager alloc] init];

in dealloc:

   self.locationManager = nil;

.h文件中的位置:

@property (nonatomic, retain) CLLocationManager *locationManager;

我认为这是一个明显的泄漏,并认为这应该解决泄漏:

self.locationManager = [[[CLLocationManager alloc] init] autorelease];

然而他声称这不会起作用,因为用他的话说:“你不自动释放类的属性。定义为保留的属性的自动生成的访问器将自动处理保留”

他让我想知道他是不是错了,或者我根本不理解记忆管理?

编辑1 :代码是

self.myName=[NSSting stringWithFormat:@"%@ is correct.", @"TechZen"];

不同
 self.locationManager = [[[CLLocationManager alloc] init] autorelease];

内存管理明智吗?

这家伙说第一个是正确的,拒绝第二个。为什么第二个会出错?据我所知,两者都将自动释放的实例分配给某些属性,但不知怎的,仍有一个顽固的论点,即第二个错误。我看不到它,任何帮助都会如此受欢迎。

7 个答案:

答案 0 :(得分:7)

在这种情况下,计算保留和释放有帮助。这绝对是一个泄漏。您的locationManager对象将被保留2次:alloc/init次呼叫一次,属性一次。将该属性设置为nil只会释放locationManager一次。

对于编辑1中给出的示例,它们确实是相同的。这听起来像其他开发人员厌恶立即自动释放或不太明白autorelease的作用。

答案 1 :(得分:4)

retain 属性选项的语义是:

  • 旧值(如果有)获取发布消息
  • 新值获取保留消息

因此,您的CLLocationManager实例在setter之后的retain-count为2。一个来自 alloc ,另一个来自保留setter。您应该在setter之后发送 release 消息:

CLLocationMamnager *aLocationManager = [[CLLocationManager alloc] init];
self.locationManager = aLocationManager;
[aLocationManager release];

或者,将其添加到自动释放池中,以便它最终至少可以免费使用。就像你自己写的那样:

self.locationManager = [[[CLLocationManager alloc] init] autorelease];

更好的是,不要使用 retain 属性选项。保留为 assign (默认值),因为您仍在使用保留对象,因此设置为go。

答案 2 :(得分:2)

以下语句保留两次,因此必须发布两次:

self.locationManager = [[CLLocationManager alloc] init];

这可能写得最好如下:

self.locationManager = [[[CLLocationManager alloc] init]autorelease];

现在,这个变量只保留了一次,你可以在你的类的dealloc函数中释放它。

此外,如果您运行以下代码行,则会调用一个版本:

locationManager = nil;

由于locationManager是合成的,当你将它设置为nil时,它会先被释放。

此外,如果您执行以下操作,将首先释放locationManager,然后在幕后重置:

self.locationManager = foo;

最后,以下内容会因exc_bad_access而崩溃,因为当您将其设置为foo时,您将双重释放locationManager:

self.locationManager = [[[CLLocationManager alloc] init]autorelease];
[locationManager release];
self.locationManager = foo;

答案 3 :(得分:1)

@jnic:你错了。 据我所知,该属性的先前值是发送消息,而不是您要分配给该属性的对象。 所以,是的,建议的代码确实泄漏了,你必须像你想的那样发送自动释放消息,ahmet emrah

答案 4 :(得分:1)

简单的经验法则:如果某个属性被标记为“保留”,请始终为其提供一个自动释放的变量(如果您正在创建它),除非您当然还需要将其保留在其他位置。

答案 5 :(得分:0)

在下方添加此行...它显然为您提供了locationManager的保留计数。这将告诉您是否需要手动释放它。

NSLog(@"retainCount:%d", [locationManager retainCount]);

编辑:

[locationManager release];

答案 6 :(得分:-2)

好的,这是交易。

当您定义类似的属性时......

@property (nonAtomic, retain) NSString myName;

...因为属性命令的默认值,它实际上就像定义它:

@property (nonAtomic, readwrite, retain, getter=getMyName,setter=setMyName) NSString myName;

当你在幕后使用@synthesize myName;时,编译器会生成一个看起来像这样的getter方法:

-(void) setMyName:(NSString *) aString{
    if (!(myString==aString) { //test if a string is the same **object** as the current myString
        if (aString != nil) { // if nil we don't want to send a retain
            [aString retain]; // increment the retain count by one
        }        
        [myString release]; //decrement the retain count of the currently assigned string by one.
        myString=nil; //set the pointer to nil to ensure we don't point to the old string object
        myString=aString; //assign the newly retained object to the myString symbol     
    }
}

正如您所看到的,来自任何先前保留计数的任何来源的任何字符串(自动释放或未自动释放)将在分配时由方法自动保留,并且当分配新值时,该方法将自动释放该字符串。多个分配不会堆叠保留计数。只要您使用生成的setter,分配的对象(在本例中为aString)将始终具有保留计数,以使其在类中保持活动状态。

这就是你可以这样做的原因......

self.myName=[NSSting stringWithFormat:@"%@ is correct.", @"TechZen"];

无需执行此操作:

self.myName=[[NSSting stringWithFormat:@"%@ is correct.", @"TechZen"] retain];

...当autoreleasepool耗尽时,不必担心字符串值是否会突然消失。

但是如果你打电话......

[self.myName release]; 

... dealloc 之外的任何地方,除非您无情地追踪它,否则属性中的对象将被填满。出于同样的原因,如果你打电话..

[self.myName retain];

...在任何地方,然后属性中的对象将泄漏(可能甚至在自身对象被释放后。)

这就是为什么我说永远不会保留或自动释放在属性中分配或复制的任何对象。它不仅毫无意义,而且反效果。因此,您只想在完成自我对象时调用属性上的释放,因为设置器有效跟踪保留计数意味着即使您仍然需要,也可以使属性无效。

属性永远不需要自动释放,因为该属性始终由self对象保留,并且任何其他对象如果使用self对象的属性,则应在内部处理保留。

一旦了解了生成的访问器内部的内容,规则就很明显了。永远不要明确地保留属性的对象。永远不要在dealloc中释放属性的对象。永远不要自动释放属性的对象。

这些规则的明显必然结果是始终在self对象内部使用self.propertyName引用,以确保自动管理属性的保留。