Objective C - ARC - 何时使用@autoreleasepool

时间:2012-02-11 22:48:36

标签: objective-c automatic-ref-counting

我正在读一点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时; 不应该被破坏吗?然而它说输出只是n被破坏了.. 有人可以解释原因吗?

他说结果和内存泄漏一样糟糕,那你怎么解决它呢?

还有最后一个问题,你什么时候应该使用@autoreleaasepool?

2 个答案:

答案 0 :(得分:13)

按行

c = nil; //Forces a release

Customer实例已取消分配,因为没有人保留它,因此输出

  

摧毁:Johnny Walker

na尚未取消分配,因为它们仍然在范围内,并且nil尚未分配给它们。

我不认为这是任何类型的内存泄漏


你通常不需要使用@autorelasepool,除非你正在做这样的事情

- (void)myMethod {
    for (int i = 0; i < 1000000; i++) {
        NSString *string = [NSString stringWithFormat:@"%d", i];
        // do something with string
    }
}

循环期间将分配超过1000000个NSString。它们将在返回方法后释放(实际上在此runloop之后),但已经消耗了太多内存。因此应该替换为

- (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

答案 1 :(得分:1)

名称和地址之间明显不同的是,您为地址创建地址对象,为名称创建NSString。在地址对象中它是@public。当客户发布时,NSString超出范围,但不是地址对象,当您释放客户时,它仍将记住给予@public NSString *城市的地址。

因此,当您为地址调用此公共值时,它仍然存在,但不是名称的NSString。要解决此问题,您可以删除地址对象的接口,该接口会释放这两个值,或者为名称创建一个接口,而不是使用NSString。