是啥增加了对象的保留计数?

Posted

技术标签:

【中文标题】是啥增加了对象的保留计数?【英文标题】:What increases an object's retain count?是什么增加了对象的保留计数? 【发布时间】:2010-11-13 22:32:39 【问题描述】:

这是我指的代码。

// Person.h

@interface Person : NSObject 
    NSString *firstName;
    NSString *lastName;

@end

// Person.m

@implementation Person
- (id)init 
    if (![super init]) return nil;
    firstName = @"John";
    lastName = @"Doe";

@end

// MyClass.m

@implementation MyClass
    .....
- (NSArray *)getPeople 
    NSMutableArray *array = [[NSMutableArray alloc] init];

    int i;
    for (i = 0; i < 10; i++) 
        Person *p = [[Person alloc] init];
        [array addObject:p];
    

    return array;

    .....
@end

现在,我知道此示例代码中没有进行内存管理。需要什么?

在 getPeople 循环中,我分配了一个 Person (retainCount 1),然后将其添加到数组中。保留计数现在是 2,对吧?如果是两个,我应该在将它添加到数组后 [p release]'ing,将 retainCount 降回 1 吗?

我说得对吗?释放方法返回的数组是调用者的责任吗? (这也将释放 Person 及其实例变量的内存,假设它们的计数为 1)。

我已经阅读了 Apple 的内存管理文档,但我想我最不清楚的是,是什么增加了对象保留计数?不过,我想我掌握了谁负责释放的想法。根据 Apple 的说法,这是基本规则:

如果您使用名称以“alloc”或“new”开头或包含“copy”的方法(例如,alloc、newObject 或 mutableCopy)创建对象,或者向其发送保留消息。您有责任放弃使用 release 或 autorelease 拥有的对象的所有权。其他任何时候您收到物品时,都不得释放它。

bobDevil 的句子“只担心您明确添加到项目的保留计数”让我点击了它。在阅读了 Apple 的所有权政策之后,本质上,创建新对象的对象/方法是负责释放 /it's/ 对它的兴趣的人。这是正确的吗?

现在,假设我是一个方法,它接收一个对象并将其分配给一个实例变量。我需要正确保留收到的对象,因为我仍然对它感兴趣?

如果其中有任何不正确的地方,请告诉我。

【问题讨论】:

【参考方案1】:

您是正确的,将保留计数添加到数组后为 2。但是,您应该只担心显式添加到项目的保留计数。

保留一个物品是一份合同,上面写着“我还没和你做完,不要走开。”一个基本的经验法则(也有例外,但通常记录在案)是在分配对象或创建副本时拥有该对象。这意味着您将获得保留计数为 1(未自动释放)的对象。在这两种情况下,您应该在完成后释放它。此外,如果您曾明确保留一个对象,则必须释放它。

因此,具体到您的示例,当您创建 Person 时,您有一个保留计数。你把它添加到一个数组中(不管它做什么,你不在乎),然后你就完成了这个人,所以你释放它:

Person *p = [[Person alloc] init]; //retain 1, for you
[array addObject:p]; //array deals with p however it wants
[p release]; //you're done, so release it

另外,正如我上面所说,您通常只在分配或复制期间拥有该对象,因此为了与另一方面保持一致,您应该返回自动释放的数组,以便 getPeople 方法的调用者执行不拥有它。

return [array autorelease];

编辑: 正确,如果你创建它,你必须释放它。如果您对它投资兴趣(通过保留),您必须释放它。

【讨论】:

【参考方案2】:

您通常/不/应该担心保留计数。这是内部实现的。你应该只关心你是否想通过保留一个对象来“拥有”它。在上面的代码中,数组应该拥有对象,而不是你(在循环之外你甚至没有引用它,除非通过数组)。因为你拥有[[Person alloc] init],所以你必须释放它。

这样

Person *p = [[Person alloc] init];
[array addObject:p];
[p release];

此外,“getPeople”的调用者不应拥有该数组。这是约定。你应该先自动释放它。

NSMutableArray *array = [[[NSMutableArray alloc] init] autorelease];

您需要阅读 Apple 关于内存管理的文档:http://developer.apple.com/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html

【讨论】:

Josh 说 NSMutableArray 默认标记为自动释放? 您必须明确自动释放您创建的对象。【参考方案3】:

当您专门调用 alloc 时,保留计数会增加,因此您需要明确释放它。

工厂方法通常会为您提供一个自动释放的对象(例如 [NSMutableArray 数组] - 您必须专门保留它以使其保持任何时间长度。)。

至于 NSArray 和 NSMutableArray addObject:,其他人将不得不发表评论。我相信您将一个类视为黑盒子,就它们如何处理自己的内存管理作为一种设计模式而言,因此您永远不会显式地释放您传递给 NSArray 的东西。当它被销毁时,它应该自己处理减少保留计数。

如果您将您的 ivars 声明为诸如 @property (retain) suchAndSuchIvar 之类的属性,并在您的实现中使用 @synthesize,您也可以获得某种隐含的保留。 Synthesize 基本上会为您创建 setter 和 getter,如果您专门调用(保留),则 setter 将保留传递给它的对象。它并不总是很明显,因为 setter 可以这样构造:

Person fart = [[Person alloc] init];
fart.firstName = @"Josh"; // this is actually a setter, not accessing the ivar
                          // equivalent to [fart setFirstName: @"Josh"], such that
                          // retainCount++

编辑:

就内存管理而言,只要将对象添加到数组中,就完成了...所以:

   for (i = 0; i < 10; i++) 
       Person *p = [[Person alloc] init];
       [array addObject:p];
       [p release];
   

乔什

【讨论】:

以上是关于是啥增加了对象的保留计数?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在启用 ARC 的项目中不需要维护保留计数

UITextView setEnabled:导致保留计数增加?

具有综合属性的 alloc + init - 是不是会导致保留计数增加 2?

如何找到对象的保留计数? [复制]

Objective-C 确定哪些对象保留另一个对象

最近创建的对象的大量保留计数。 Objective-C