Цель C - ARC - Когда использовать @autoreleasepool - PullRequest
10 голосов
/ 12 февраля 2012

Я немного читал об 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 ]

13 голосов
/ 12 февраля 2012

по линии

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

1 голос
/ 22 октября 2012

Очевидное различие между именем и адресом состоит в том, что вы создаете объект адреса для адреса и NSString для имени. В объекте адреса это @public. Это означает, что строка NSString выходит из области действия, когда клиент освобожден, но не является объектом адреса, он все равно будет помнить адрес, присвоенный городу @public NSString *, когда вы освобождаете клиента.

Таким образом, когда вы вызываете это открытое значение для адреса, оно все еще там, но не NSString для имени. Чтобы это исправить, либо удалите интерфейс объекта Address, который освобождает оба значения, либо создайте один интерфейс для имени вместо использования NSString.

...