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

Iphone 在Objective-C中自动释放是如何工作的?,iphone,objective-c,memory,Iphone,Objective C,Memory,我正在通读指南 我有点被这段代码弄糊涂了: - (void)printHello { NSString *string; string = [NSString stringWithFormat:@"Hello"]; NSLog(@"%@", string); } 在我看来,字符串的引用计数将为0。这是真的吗 在调用NSLog(string)之前,是什么阻止字符串被释放 这是否等同于: - (void)printHello { NSString *string;

我正在通读指南

我有点被这段代码弄糊涂了:

- (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);
}

编辑:类似地,指南中给出了此代码:

何时以及如何释放返回值?谁是主人?
fullName
的调用方是否需要释放由fullName返回的字符串

首先:

NSLog(string);
不要这样做。(我刚刚意识到它来自Apple docs.wird。)
NSLog
的第一个参数是格式化字符串。如果您的
字符串
包含某些百分比的转义,则会发生不好的事情。如果稍微长一点,正确的方法是:

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]];
}
这非常方便,并且使手动内存管理非常愉快。您可能对和感兴趣。

严格地说

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

- (void)printHello {
    NSString *string;
    string = [[[NSString stringWithFormat:@"Hello"] retain] autorelease];
    NSLog(@"%@", string);
}
约定是一个方法应该自动释放它返回的任何对象。唯一的例外(AFAIK)是构造函数,它返回一个带有+1 retain计数的对象。因为
[NSString stringWithFormat:
返回一个对象。在第一个代码段中,
stringWithFormat:
返回一个已经自动删除的对象。第二个代码段,您将再次保留它,它将被释放两次(这具有相同的效果,但第二个retain/autorelease对是多余的)

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


我强烈建议您阅读《内存管理指南》(Memory Management Guide)中的这一章(顺便说一句,这一章紧随其后)。

事实上,这段代码取自苹果公司自己的文档。我向他们抱怨NSLog。你也应该这么做。我认为你也应该——因为这里没有提到iPhone——要小心一点,当有垃圾收集器时,对象也可能在其他上下文中被释放。你没有指明你是指iPhone还是Mac,这是有区别的,因为Mac有垃圾收集器。
- (void)printHello {
    NSString *string;
    string = [[[NSString stringWithFormat:@"Hello"] retain] autorelease];
    NSLog(@"%@", string);
}