【问题标题】:iPhone memory management (with specific examples/questions)iPhone 内存管理(带有具体示例/问题)
【发布时间】:2011-05-31 10:37:52
【问题描述】:

大家好。我知道有人问过这个问题,但我仍然没有清楚地了解 Objective-C 中的内存管理。我觉得我已经很好地掌握了它,但我仍然想要以下代码的一些正确答案。我有一系列的例子希望有人能澄清一下。

为实例变量设置值

假设我有一个NSMutableArray 变量。在我的课堂上,当我初始化它时,我需要在上面调用retain 吗?

我做吗

fooArray = [[[NSMutableArray alloc] init] retain];

fooArray = [[NSMutableArray alloc] init];

[[NSMutableArray alloc] init] 是否已经将retain 计数设置为1,所以我不需要调用retain 吗?另一方面,如果我调用一个我知道返回autoreleased 对象的方法,我肯定必须调用retain,对吧?像这样:

fooString = [[NSString stringWithFormat:@"%d items", someInt] retain];

属性

我询问retain 是因为我对@property 的自动设置器的工作原理有点困惑。

如果我将fooArray 设置为@property 并设置retain,Objective-C 会自动创建以下设置器,对吗?

- (void)setFooArray:(NSMutableArray *)anArray {
    [fooArray release];
    fooArray = [anArray retain];
}

所以,如果我有这样的代码:self.fooArray = [[NSMutableArray alloc] init];(我相信这是有效代码),Objective-C 会创建一个 setter 方法,该方法在分配给 fooArray 的值上调用 retain。在这种情况下,retain 计数实际上是否为 2?

设置属性值的正确方法

我知道有关于这个和(可能)辩论的问题,但是设置@property 的正确方法是什么?

这个?

self.fooArray = [[NSMutableArray alloc] init];

还是这个?

NSMutableArray *anArray = [[NSMutableArray alloc] init];
self.fooArray = anArray;
[anArray release];

我很想对这些示例进行一些说明。谢谢!

【问题讨论】:

    标签: iphone objective-c ios memory-management


    【解决方案1】:

    首先,用这一行:

    fooArray = [[NSMutableArray alloc] init];
    

    fooArray 将自动保留计数为 1。

    第二,是的,是 2。而且你对 setter 实现的猜测是正确的。

    第三,后一个是对的

    【讨论】:

      【解决方案2】:

      根据 Apple 的Object Ownership Policy,任何以allocnew 开头或包含copy 的方法都归调用者所有。

      要获得一个对象的所有权,你必须retain它。

      因此,在您的第一个示例中,retain 是不必要的,因为您已经拥有该对象。

      正确的做法:

      fooArray = [[NSMutableArray alloc] init];
      

      由于autoreleased 对象归当前自动释放池所有,因此您必须对它们调用retain 以获得对它们的所有权,所以这个例子是正确的:

      fooString = [[NSString stringWithFormat:@"%d items", someInt] retain];
      

      这也可以正常工作:

      self.fooString = [NSString stringWithFormat:@"%d items", someInt]; //retained by property setter
      

      对于您使用属性设置器的最后一个示例,这将是正确的方法:

      NSMutableArray *anArray = [[NSMutableArray alloc] init];
      self.fooArray = anArray;
      [anArray release];
      

      我建议以下解决方案,而不是必须执行上述操作:

      self.fooArray = [NSMutableArray arrayWithCapacity:10];
      

      arrayWithCapacity: 将返回一个自动释放的NSMutableArray,它是由属性设置器方法生成的retain。 :)

      【讨论】:

      • 我知道创建临时数组的对象来分配属性数组很好,但是与 self.fooArray = [[NSMutableArray alloc] init] 相关的问题是什么;为什么它会导致内存泄漏。
      • @Ishu Gupta,那里存在内存泄漏,因为您已经拥有该对象,因为您在该行上调用了 alloc,并且您的 @property 设置器可能具有 retain 修饰符,所以该分配的最终结果变为self.fooArray = [[[NSMutableArray alloc] init] retain],因此泄漏。
      • @Ishu Gupta 我很高兴能帮上忙 :)
      • 这真是一个很好的解释。谢谢雅各布。我的一些疑问现在很清楚了。
      【解决方案3】:

      理想情况下,您应该尽可能使用访问器,尤其是在处理对象时,因为它们有助于避免许多内存问题。所以即使是实例变量,你也应该这样做:

      self.fooArray = ...;
      

      而不是

      fooArray = ...;
      

      你应该为对象实例变量声明属性的原因是因为内存管理稍微复杂一些,并且每次手动重新创建它很棘手。非原子的保留属性的正确设置器如下所示:

      - (void)setFoo:(NSArray *)aFoo {
          if (foo == aFoo) {
              return;
          }
          NSArray *oldFoo = foo;
          foo = [aFoo retain];
          [oldFoo release];
      }
      

      当你做这样的事情时,你对实例变量的保留计数为 2 是正确的(假设 foo 被保留):

      self.foo = [[NSMutableArray alloc] init];
      

      第一个保留计数来自alloc,第二个来自您的综合设置器。这些中的任何一个都应该起作用:

      // longer, explicit version, releases immediately (more efficient)
      NSMutableArray *aFoo = [[NSMutableArray alloc] init];
      self.foo = aFoo;
      [aFoo release];
      
      // autoreleased, not so bad unless you're a memory management freak
      self.foo = [[[NSMutableArray alloc] init] autorelease];
      
      // an even shorter version of the above
      self.foo = [NSMutableArray array];
      

      要创建私有属性,您可以在 .m 实现文件中将它们声明为类扩展。举个例子,考虑一个简单的 Person 对象,它有一个名称和一个布尔属性 didSave,它简单地指示该对象是否已保存到某个数据库中。由于我们不想将其暴露给外界,但仍然在实现文件中保留属性的好处,我们可以创建头文件将所有实例变量(公共、私有、受保护)和只有公共属性:

      // Person.h
      
      @interface Person {
          NSString *name;
      
          @private
          BOOL didSave;
      }
      
      @property (nonatomic, retain) NSString *name;
      
      @end
      

      但是在实现中声明私有属性:

      // Person.m
      
      // property is declared as a class extension, making it 
      // invisible to the outside world.
      @interface Person ()
          @property BOOL didSave;
      @end
      
      @implementation
      
      // synthesize as normal
      @synthesize name, didSave;
      
      @end
      

      【讨论】:

      • 感谢您的回复,阿努拉格。当我不需要其他类可以访问变量时,我一直在尝试避免使用属性。有点像 n00b 的问题,但是......我可以拥有其他类无法访问的属性吗?
      • 感谢您的编辑,阿努拉格。如果你不介意,我想问几个澄清点。你能在.m 的@interface 中声明canSave 变量吗?在我看来,未公开的变量已经隐含私有,并且必须将其标记为这样似乎有点多余。
      • 我听到你谈到私有实例变量部分,但不幸的是,这是我们可以通过 Objective-C 保持实现细节私有的最接近的方法。如果我们不将变量标记为私有,那么默认情况下它们具有受保护的访问权限,这可能是也可能不是。
      猜你喜欢
      • 2011-10-19
      • 2011-01-07
      • 2011-04-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-06-06
      • 2010-10-20
      • 1970-01-01
      相关资源
      最近更新 更多