我正在阅读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
的调用者是否需要释放全名返回的字符串?
答案 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 loops和Objective-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章后面)。