【问题标题】:iPhone: Is this a leak or notiPhone:这是否泄漏
【发布时间】:2011-01-16 04:56:09
【问题描述】:

最近 Stack Overflow 上有人告诉我,下面的代码不会泄漏,属性会自行处理保留:

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

在dealloc中:

   self.locationManager = nil;

.h 文件的位置:

@property (nonatomic, retain) CLLocationManager *locationManager;

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

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

但是他声称这行不通,因为用他的话来说:“您不会自动释放类的属性。定义为保留的属性的自动生成访问器将自动处理保留”

他让我怀疑他是错的还是我根本不了解内存管理?

EDIT 1:是代码

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

任何不同于

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

内存管理方面?

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

【问题讨论】:

  • 在您的 EDIT 1 中,保留计数明智,它们是相同的。这些对象都只保留一次。在所有便利功能中都有一个隐式的自动释放。如果您在 init 语句中没有看到 alloc 一词,则该变量是自动释放的。如果你使用alloc,你应该使用autorelease

标签: iphone memory-management memory-leaks


【解决方案1】:

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

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

编辑:

[locationManager release];

【讨论】:

  • 不,不,我是在概念上问。此外,NSlogging 的 retainCounts 并不是一个很好的泄漏衡量标准。
  • 哦,好的。最佳实践应该在 dealloc 中执行此操作,因为当自动释放池耗尽时,您的对象不会被释放。以上编辑。
  • 来自 NSObject 类参考:“重要提示:此方法 [retainCount] 通常在调试内存管理问题时没有任何价值 [...] 您不太可能从该方法中获得有用的信息。”
  • 我不同意 jnic - 但我同意它有时不准确,尤其是在 NSString 对象上
【解决方案2】:

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

【讨论】:

    【解决方案3】:

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

    对于编辑 1 中给出的示例,它们确实是相同的。听起来其他开发人员不喜欢立即自动发布,或者不太了解 autorelease 的作用。

    【讨论】:

      【解决方案4】:

      retain 属性选项的语义是:

      • 旧值(如果有)获得释放消息
      • 新值获得保留消息

      因此,在设置器之后,您的 CLLocationManager 实例的保留计数为 2。一个来自 alloc,一个来自保留设置器。您应该在 setter 之后立即发送 release 消息:

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

      或者,将它添加到自动释放池中,这样它最终至少会被释放。就像你自己写的那样:

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

      更好的是,不要使用 retain 属性选项。将其保留为 assign(默认值),因为无论如何您都在使用保留对象,所以您已经准备好了。

      【讨论】:

        【解决方案5】:

        好的,这就是交易。

        当你像这样定义一个属性时......

        @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 对象时,您只想在属性上调用 release,因为 setter 对保留计数的有效跟踪意味着即使您可能仍然需要该属性,您也可以 nil 。

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

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

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

        【讨论】:

        • 老兄,self.myName=[NSSting stringWithFormat:@"%@ 是正确的。", @"TechZen"];与 self.locationManager = [[[CLLocationManager alloc] init] autorelease]; 完全相同内存管理方面。我不敢相信你怎么看不到它。
        【解决方案6】:

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

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

        最好这样写:

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

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

        此外,如果您运行以下代码行,则会调用发布:

        locationManager = nil;
        

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

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

        self.locationManager = foo;
        

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

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

        【讨论】:

          【解决方案7】:

          简单的经验法则:如果一个属性被标记为“保留”,总是给它一个自动释放的变量(如果你正在创建它),除非你当然还需要在其他地方保留它。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2016-09-20
            • 1970-01-01
            • 1970-01-01
            • 2010-11-25
            • 2018-04-28
            • 1970-01-01
            相关资源
            最近更新 更多