6. Программирование на Objective-C. Управление памятью



До Xcode 4.2 управление памятью осуществлялось вручную.

В Java, Ruby, Python управлением памятью занимается сборщик мусора.

В Objective-C тоже есть подобная штука, но она не работает на iPhone, iPad, Mac OS X ниже Leopard.


Сборщика мусора по умолчанию в проекте нет.


Управление памятью вручную.

NSString *name = [NSString alloc];
// вы владеете объектом, если создали его или скопировали - случайно это не происходит
// retainCount = 1 (счетчик ссылок объекта)

[name init];

[anotherObject firstMethod:name];
// retainCount = ?

[name release];
// retainCount = 0
name = nil; // в Objective-C nil-объекты игнорируют посылваемые им сообщения

Возможные проблемы:
  • указатель ссылается на объект которого уже нет;
  • объект есть, а указателя на него нет - утечка памяти.

alloc, new, copy, retain должны быть согласованы с вызовом метода release.
Оборотная сторона правила если вы не являетесь владельцем объекта, то и не вам его освобождать.

ARC сам создает и заполняет метод dealloc, однако все еще сохраняется возможность самому написать этот метод для управления внешними ресурсами.

Как реализовать деструктор для класса?

How do I know whether the compiler has ARC support enabled?

Как включается/выключается ARC в проекте:


objective c - NSString retain Count - Stack Overflow
objective c - Reusing a NSString variable - does it cause a memory leak? - Stack Overflow

После вызова release значение releaseCount бесполезно и может не соответствовать действительности.

Вообще retainCount довольно таки глючная штука и лучше ее не использовать.

Пример ручного управления памятью (ARC выкл.):

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[])
{
    NSDate *theMyDate; // nil
    NSString  *theMyString; // nil

#if !__has_feature(objc_arc)
    
    theMyDate = [NSDate date];
    NSLog(@"theMyDate.retainCount = %lu", (unsigned long)[theMyDate retainCount]); // [theMyDate retainCount] == 1
    theMyString = [[NSString alloc] initWithFormat:@"Hello, World! Today is %@.", theMyDate];
    NSLog(@"theMyString.retainCount = %lu", (unsigned long)[theMyString retainCount]); // [theMyString retainCount] == 1
    [theMyString release];
    theMyString = nil;
    [theMyDate release];
    theMyDate = nil;

    theMyDate = [NSDate new]; // NSDate *theMyDate = [[NSDate alloc] init];
    NSLog(@"theMyDate.retainCount = %lu", (unsigned long)[theMyDate retainCount]); // [theMyDate retainCount] == 1
    [theMyDate release];
    theMyDate = nil;

#else

#endif
    return 0;
}


Пример полуавтоматического управления памятью (ARC выкл.):

#import <Foundation/Foundation.h>

int main(int argc, const char * argv[])
{
    NSDate *theMyDate; // nil
    NSString  *theMyString; // nil

#if !__has_feature(objc_arc)
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
        theMyDate = [NSDate date]; // здесь autorelease не нужен потому что не мы создаем объект!
        NSLog(@"theMyDate.retainCount = %lu", (unsigned long)[theMyDate retainCount]); // [theMyDate retainCount] == 1
        theMyString = [[[NSString alloc] initWithFormat:@"Hello, World! Today is %@.", theMyDate] autorelease];
        NSLog(@"theMyString.retainCount = %lu", (unsigned long)[theMyString retainCount]); // [theMyString retainCount] == 1
    
        theMyDate = [[NSDate new] autorelease]; // NSDate *theMyDate = [[NSDate alloc] init];
        NSLog(@"theMyDate.retainCount = %lu", (unsigned long)[theMyDate retainCount]); // [theMyDate retainCount] == 1
    [pool drain];

#else
#endif
    return 0;
}



В этом примере вместо блока:
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
// ...
[pool drain];
можно было бы воспользоваться альтернативным блоком:
@autoreleasepool
{
// ...
}

Это не совсем хороший пример потому что в главном потоке autorelease пул создается по умолчанию при входе в функцию и освобождается на выходе.  Как в режиме MRC, так и в ARC. Поэтому тут можно было опустить создание этого блока. В случае отсутствия текущего пула в стеке (для периферийного потока стек изначально пуст) в консоль будет выведено сообщение об ошибке.

Is @autoreleasepool needed in ARC mode? · Issue #1 · gologo13/objective-c-practice

У вас может возникнуть вопрос почему директива autoreleasepool работает в режиме ARC, а NSAutoreleasePool и сообщения autorelease не допустимы.
Не нужно путать ARC со сборщиком мусора. Это разные вещи. ARC осуществляет ручное управление за вас, т.е. сам нижнем уровне оперирует с retain, release, и autorelease. Подробнее...