增加一个对象的保留计数的因素是什么?

13

这是我参考的代码。

// 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),然后将其添加到数组中。这时的retain count是2吗?如果是两个,那么在将其添加到数组后,我应该[ p release],将retainCount降回到1吗?

我想问一下,是否正确的做法是由调用者负责释放方法返回的数组?(假设它们的计数都为1,则还会释放Person及其实例变量的内存)。

我已经阅读了苹果的内存管理文档,但我最不清楚的是什么会增加对象的保留计数?尽管我想我理解了谁负责释放的概念。这是苹果的基本规则:

如果您使用以“alloc”或“new”开头或包含“copy”的方法创建对象(例如, alloc,newObject或 mutableCopy),或者发送retain消息,则拥有该对象。您负责使用release或autorelease放弃拥有对象。任何其他时间收到对象,您不能释放它。

bobDevil的句子“只关心您明确添加到项中的retain counts”让我恍然大悟。在阅读苹果的所有权政策后,本质上,创建新对象的对象/方法负责释放或释放其对它的兴趣。这个理解正确吗?

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

如果有任何不正确的地方,请让我知道。

3个回答

19

在将对象添加到数组后,您正确地指出保留计数为2。但是,您只需要担心您显式添加到该项的保留计数。

保留一个对象是一种合同,它表示“我还没有完成你,不要离开”。一个基本的经验法则(有例外情况,但通常会有文档记录)是,在分配对象或创建副本时,您拥有该对象。这意味着您获得具有1个保留计数(非自动释放)的对象。在这两种情况下,当您完成后应释放该对象。此外,如果您明确保留对象,则必须释放它。

因此,针对您的示例,当您创建Person对象时,它的保留计数为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

同样的,就像我之前所说的,通常只有在alloc或copy期间你才拥有对象,因此为了保持一致,你应该将数组以自动释放的形式返回,这样getPeople方法的调用者就不会拥有它。

return [array autorelease];

编辑: 正确的做法是,如果你创建了它,就必须释放它。如果你对它感兴趣(通过保留),你也必须释放它。


5
保留计数在调用alloc时会增加,因此您需要显式释放。工厂方法通常会提供自动释放的对象(例如[NSMutableArray array] - 您需要明确保留该对象以使其保持一段时间)。至于NSArray和NSMutableArray addObject:,其他人将不得不发表评论。我认为,从设计模式的角度来看,您将类视为黑盒子,因此您永远不会显式释放已传递给NSArray的任何内容。当它被销毁时,它应该自己处理减少保留计数。如果您将实例变量声明为@property(retain)suchAndSuchIvar,并在实现中使用@synthesize,则还可以获得某种隐式保留。synthesize基本上为您创建setter和getter,如果您明确调用(retain),则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];
   }

乔希


2

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

因此

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

此外,“getPeople”的调用者不应该拥有该数组。这是惯例。你应该首先将其自动释放。
NSMutableArray *array = [[[NSMutableArray alloc] init] autorelease];

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

。请注意,本文档是针对IT技术相关内容的,如果您有任何疑问,请随时联系我们。


Josh说NSMutableArray默认被标记为自动释放? - Thomas R
2
你必须显式地释放你创建的对象。 - mjhoy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接