Objective C - ARC - 何时使用@autoreleasepool
Posted
技术标签:
【中文标题】Objective C - ARC - 何时使用@autoreleasepool【英文标题】:Objective C - ARC - When to use @autoreleasepool 【发布时间】:2012-02-11 22:48:35 【问题描述】:我正在阅读一些关于 ARC 的文章,我看到了这个:
@interface Address : NSObject
@public
NSString *city;
@end
@implementation Address
- (Address*) init: (NSString*) c
city = c;
return self;
- (void) dealloc
NSLog(@"Destroying address: %@", city);
@end
@interface Customer : NSObject
NSString *name;
Address *addr;
@end
@implementation Customer
- (Customer*) init: (NSString*) n withAddress: (Address*) a
//Note 1: Automatic retain on assignment
name = n;
addr = a;
return self;
- (void) dealloc
NSLog(@"Destroying: %@", name);
//Note 2: Automatic release of member variables
@end
Customer* objectReturnTest()
NSString * n = [[NSString alloc] initWithString: @"Billy Bob"];
Address * a = [[Address alloc] init: @"New York City"];
Customer *c = [[Customer alloc] init: n withAddress: a];
//Note 3: ARC will put the returned object in autorelease pool.
return c;
A couple of basic things to note here. As "Note 1" says, when an object is assigned to a variable, a call to retain is made automatically. This increments the reference count. As "Note 2" says, when an object is destroyed, all member variable objects are released for you. You no longer have to do that from the dealloc method.
Finally, when a method returns a newly created object, ARC will put the returned object in an autorelease pool. This is stated in "Note 3".
Now, let’s use the code.
int main (int argc, const char * argv[])
NSString * n = [[NSString alloc] initWithString: @"Johnny Walker"];
Address * a = [[Address alloc] init: @"Miami"];
Customer *c = [[Customer alloc] init: n withAddress: a];
NSLog(@"Before force release");
c = nil; //Force a release
NSLog(@"After force release");
@autoreleasepool
Customer *c2 = objectReturnTest();
NSLog(@"After autorelease pool block.");
return 0;
The log output from this code will be:
Before force release
Destroying: Johnny Walker
After force release
Destroying: Billy Bob
Destroying address: New York City
After autorelease pool block.
Destroying address: Miami
A couple of things to note here. See how force release works. We set a variable to nil. ARC immediately releases the reference count. This causes the Customer object "Johnny Walker" to get destroyed. But, the member Address object "Miami" doesn’t get destroyed. This object gets destroyed at the very end of the main method. This is an extremely odd and non-intuitive behavior. Technically, this is not a memory leak, but, in reality member variables can pile up and take up a lot of memory. This is just as bad as memory leak.
The object return test works as expected. Customer "Billy Bob" is put in auto release pool. At the end of the @autoreleasepool block, the pool is drained and the object is released.
看这部分;
int main (int argc, const char * argv[])
NSString * n = [[NSString alloc] initWithString: @"Johnny Walker"];
Address * a = [[Address alloc] init: @"Miami"];
Customer *c = [[Customer alloc] init: n withAddress: a];
NSLog(@"Before force release");
c = nil; //Force a release
NSLog(@"After force release");
@autoreleasepool
Customer *c2 = objectReturnTest();
NSLog(@"After autorelease pool block.");
return 0;
当他做 c = nil; c a 和 n 不应该都被销毁吗?然而它说输出只是 n 被破坏了.. 谁能解释一下原因?
他说结果和内存泄漏一样糟糕,那你怎么解决呢?
最后一个问题,你应该什么时候使用@autoreleaasepool?
【问题讨论】:
blog.webagesolutions.com 【参考方案1】:逐行
c = nil; //Forces a release
Customer
实例被释放,因为没有人保留它,因此输出为
毁灭:强尼沃克
但n
和a
尚未被释放,因为它们仍保留在范围内且nil
尚未分配给它们。
我不认为这是任何形式的内存泄漏
你通常不需要使用@autorelasepool,除非你正在做这样的事情
- (void)myMethod
for (int i = 0; i < 1000000; i++)
NSString *string = [NSString stringWithFormat:@"%d", i];
// do something with string
在循环期间将分配超过 1000000 个 NSString。它们将在方法返回后(实际上是在此运行循环之后)被释放,但已经消耗了太多内存。因此应该替换为
- (void)myMethod
for (int i = 0; i < 1000000; i++)
@autoreleasepool
NSString *string = [NSString stringWithFormat:@"%d", i];
// do something with string
您应该阅读本文以了解有关内存管理的更多信息 https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html#//apple_ref/doc/uid/20000047-CJBFBEDI
【讨论】:
声明字符串的时候不应该加上autorelease吗? 还有一件事,如果你想要一个函数返回一些东西,比如一个字符串怎么办?你只是做 NSString *a = @"hey";返回一个;?不会释放 a 并且您将无法保存该值吗? @user1021085[NSString stringWithFormat:]
与[[[NSString alloc] initWithFormat:] autorelease]
相同,所以它是一个自动释放的对象。 ARC 可以处理所有事情(除非您正在处理 __unsafe_unretained 指针或 __bridge cast),因此它将保留并自动释放字符串,并且在下一次运行循环之前不会被释放
我不明白的是,在循环的第二次运行中,字符串指向一个新字符串,而前一个已准备好被释放。为什么要等到方法结束才这样做?内存中所有这些无指针字符串有什么用。我们必须通过添加 @autoreleasepool 来强制执行此行为似乎有点奇怪。
@Zyphrax 因为它是自动释放对象 ([[[NSString alloc] init] autorelease]
),自动释放池负责释放它。没有@autoreleasepool
,只有你的代码会在循环中执行,自动释放将没有机会释放内存。【参考方案2】:
name 和 address 之间的明显区别在于您为地址创建了一个地址对象,为名称创建了一个 NSString。在地址对象中它是@public。这意味着释放客户时 NSString 超出范围,但不是地址对象,它仍然会记住释放客户时给@public NSString *city 的地址。
所以当你调用地址的这个公共值时,它仍然存在,但不是名称的 NSString。要解决此问题,您可以删除地址对象的接口,该接口会释放两个值,或者您为名称创建一个接口而不是使用 NSString。
【讨论】:
以上是关于Objective C - ARC - 何时使用@autoreleasepool的主要内容,如果未能解决你的问题,请参考以下文章
从文档目录中的文件夹创建 ZIP 文件 - Objective C (ARC)
c_cpp Singleton(Objective-C with ARC)
带有@autorelease和ARC的Objective C For循环
Objective C:何时在 App Delegate 中使用方法以及何时在 View Controller 中使用方法