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 实例被释放,因为没有人保留它,因此输出为

毁灭:强尼沃克

na 尚未被释放,因为它们仍保留在范围内且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的主要内容,如果未能解决你的问题,请参考以下文章

深入理解Objective C的ARC机制

从文档目录中的文件夹创建 ZIP 文件 - Objective C (ARC)

Objective-C关于非ARC模式下的对象引用计数

c_cpp Singleton(Objective-C with ARC)

带有@autorelease和ARC的Objective C For循环

Objective C:何时在 App Delegate 中使用方法以及何时在 View Controller 中使用方法