自动释放如何在Objective-C中工作?

时间:2010-12-04 13:41:54

标签: iphone objective-c memory

我正在阅读Practical Memory Management指南。

我对这段代码感到有些困惑:

- (void)printHello {
    NSString *string;
    string = [NSString stringWithFormat:@"Hello"];
    NSLog(@"%@", string);
}

在我看来,字符串的引用计数为0.这是真的吗?

在我们致电NSLog(string)之前,什么阻止字符串被解除分配?

这在某种程度上等同于:

- (void)printHello {
    NSString *string;
    string = [[[NSString stringWithFormat:@"Hello"] retain] autorelease];
    NSLog(@"%@", string);
}

修改:同样,此代码在Practical Memory Management 指南中提供:

- (NSString *)fullName {
    NSString *string = [NSString stringWithFormat:@"%@ %@", firstName, lastName];
    return string;
}

何时以及如何释放返回值?谁是老板? fullName的调用者是否需要释放全名返回的字符串?

2 个答案:

答案 0 :(得分:8)

首先:

NSLog(string);

不要这样做。 (我刚刚意识到它来自Apple文档。很奇怪。)NSLog的第一个参数是格式化字符串。如果你的string包含一些百分比逃脱,那么就会发生不好的事情。正确的,如果稍长的方式是:

NSLog(@"%@", string);

现在要点:自动释放的对象没有保留计数。他们保留了1+的数量,并且对他们进行了未决-1的操作,将在“将来很快”发生。

“很快将来”的确切含义取决于具体情况。如果您在主线程上并且没有其他自动释放池,则自动释放的对象将在下一个runloop迭代中释放。如果您有一个额外的发布池,则不一定是这种情况:

// Let’s pretend this is a long loop and you don’t want to wait
// for the autoreleased objects to be collected by the main pool.
for (…) {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
    NSString *foo = [NSString stringWith…];
    [pool drain];
    // Now foo is no longer valid.
}

对于返回自动释放的对象,这是自动释放的主要用例之一。你正在返回一个将很快消失的对象,但如果调用者感兴趣,他可以保留并接管所有权。 (就像你赦免图像一样,用燃烧的安全保险丝传递炸弹。如果来电者有兴趣,他会通过保留来放出保险丝。)如果来电者不感兴趣,可能他会忽略输出从一个函数或只是使用该值来构造一些其他对象,他没有做任何事情,对象将失去内存:

- (id) createObject {
    return [NSString stringWith…];
}

- (void) interestedCaller {
    NSString *value = [[self createObject] retain];
}

- (void) notInterestedCaller {
    [self createObject]; // maybe just interested in side effects
    NSString *differentString = [NSString stringWithString:[self createObject]];
}

这非常方便,使手动内存管理非常愉快。您可能对run loopsObjective-C tutorial by Scott Stevenson感兴趣。

答案 1 :(得分:8)

严格来说,

- (void)printHello {
    NSString *string;
    string = [NSString stringWithFormat:@"Hello"];
    NSLog(@"%@", string);
}

不等于

- (void)printHello {
    NSString *string;
    string = [[[NSString stringWithFormat:@"Hello"] retain] autorelease];
    NSLog(@"%@", string);
}

约定是一个方法应该自动释放它返回的任何对象。唯一的例外(AFAIK)是构造函数,它返回一个具有+1保留计数的对象。由于[NSString stringWithFormat:]返回一个对象。在第一个片段中,stringWithFormat:返回已经自动释放的对象。第二个片段,你再次保留它并且它将被释放两次(具有相同的效果,但第二个保留/自动释放对是多余的)。

好的,现在回答你的问题。基本上,每次UIKit调用您的代码时,它都会创建一个NSAutoreleasePool对象。每次自动释放对象时,都会将其添加到此池中。最后,当您的代码返回到UIKit时,它会调用池上的drain方法(即[pool drain])并释放已添加到池中的每个对象并释放池。此外,自动释放池可以嵌套,因此如果您要创建大量自动释放的对象,则可以创建自己的池并将其耗尽。它并不像听起来那么复杂。

我强烈建议您阅读“内存管理指南”中的Autorelease Pools章节(顺便提一下,就在Practical Memory Management章后面)。