【问题标题】:How does autoreleasing work in Objective-C?自动发布在 Objective-C 中是如何工作的?
【发布时间】:2011-05-20 05:17:35
【问题描述】:

我正在阅读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的调用者需要释放全名返回的字符串吗?

【问题讨论】:

  • 你没有说明你是指 iPhone 还是 Mac,这是有区别的,因为 Mac 有垃圾收集。

标签: iphone objective-c memory


【解决方案1】:

严格来说,

- (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:] 返回一个对象。在第一个 sn-p 中,stringWithFormat: 返回一个已经自动释放的对象。第二个 sn-p,您将再次保留它,它将被释放两次(具有相同的效果,但第二个保留/自动释放对是多余的)。

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

我强烈建议您阅读内存管理指南中的 Autorelease Pools 章节(顺便说一下,紧随 Practical Memory Management 章节之后)。

【讨论】:

    【解决方案2】:

    首先:

    NSLog(string);
    

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

    NSLog(@"%@", string);
    

    现在重点:自动释放对象的保留计数不为零。它们的保留计数为 1+,并对它们进行待处理的 –1 操作,该操作将在“不久的将来”发生。

    “不久的将来”的确切含义取决于具体情况。如果你在主线程上并且没有额外的自动释放池,自动释放的对象将在下一次运行循环迭代中释放。如果您有额外的发布池,则不必如此:

    // 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 loops 和Objective-C tutorial by Scott Stevenson 感兴趣。

    【讨论】:

    • 事实上,该代码取自 Apple 自己的文档。我向他们抱怨 NSLog。你也应该这样做。
    • 我认为人们也应该——因为没有提到 iPhone——在这里要小心一点,当有垃圾收集器时,也可能会在其他上下文中释放对象。
    猜你喜欢
    • 1970-01-01
    • 2011-08-04
    • 2011-10-05
    • 2010-11-16
    • 1970-01-01
    • 2012-06-25
    • 2011-07-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多