Выпуск Objective C, авто-выпуск и типы данных - PullRequest
25 голосов
/ 02 августа 2009

Я новичок в коде, управляемом памятью, но довольно хорошо понимаю идею.

Проходя мое приложение через инструмент утечки в XCode, я заметил, что мне нужно было только очистить свои пользовательские объекты, но не динамически создаваемые массивы, например, поэтому я решил, что эти типы данных автоматически высвобождаются - имеет смысл, так как мне нужно было только освободить массивы, которые я использовал как свойства, которые были (сохранены) на них.

Затем я заметил кое-что странное: у меня возникла утечка в определенном массиве, инициализированном так:

NSMutableArray *removals = [NSMutableArray new];

но не похожий

NSMutableArray *removals = [NSMutableArray arrayWithCapacity:9];

Теперь, причина, по которой был настроен «новый», состоит в том, что в нем может быть 0–99 элементов, тогда как другой, который я знал, будет всегда равным 9. Поскольку оба массива передаются тому же методу позже основанный на взаимодействии с пользователем, я либо получал утечку, если я не выпускал в конце метода, либо исключение, если я делал!

Я изменил первый массив на

NSMutableArray *removals = [NSMutableArray arrayWithCapacity:99];

и у меня нет утечек, и мне не нужно ничего выпускать. Кто-нибудь может объяснить?

Ответы [ 3 ]

65 голосов
/ 03 августа 2009

Как отмечено в правилах управления памятью , всякий раз, когда у вас есть объект, который вы создали с помощью +alloc, +new, -copy или -mutableCopy, вы владеете им и несете ответственность для выпуска его в какой-то момент. (На самом деле, +new - это просто сокращение для [[MyClass alloc] init].) Как вы заметили, создание массива через [NSArray new] без его освобождения является утечкой памяти. Однако, если вы правильно обрабатываете этот объект, в какой-то момент его можно отпустить. Например:

  • Если метод, который использует массив, вызывается из в метод, который создает массив, то вы сможете освободить массив после того, как он был использован , Если внутреннему методу необходимо сохранить более постоянную ссылку на массив, то этот метод отвечает за отправку -retain и, в конечном итоге, -release объекту. Например:

    - (void)myMethod {
        NSArray *removals = [NSArray new];
        // ...
        [someObject someOtherMethod:removals];
        [removals release];
    }
    
  • Если вы создали массив в методе -init для объекта, то метод -dealloc может освободить его при уничтожении объекта.

  • Если вам нужно создать массив, а затем вернуть его из метода, вы обнаружили причину, по которой было изобретено автоматическое освобождение. Вызывающий ваш метод не несет ответственности за освобождение объекта, так как это не метод +alloc, +new, -copy или -mutableCopy, но вы должны убедиться, что он в конечном итоге будет освобожден. В этом случае вы вручную вызываете -autorelease для объекта, прежде чем вернуть его. Например:

    - (NSArray *)myMethod {
        NSArray *removals = [NSArray new];
        // ...
        return [removals autorelease];
    }
    

Когда вы создаете массив с помощью +arrayWithCapacity:, вы не вызываете один из «специальных» методов, поэтому вам не нужно освобождать результат. Это, вероятно, реализовано с -autorelease, очень похоже на последний пример выше, но не обязательно. (Кстати, вы также можете создать пустой автоматически выпущенный NSMutableArray с [NSMutableArray array]; метод найден в NSArray, поэтому он не будет отображаться в документации по NSMutableArray, но он создаст изменяемый массив при отправке в класс NSMutableArray. ) Если вы собираетесь возвращать массив из вашего метода, вы можете использовать это как сокращение для [[[NSMutableArray alloc] init] autorelease], но это всего лишь ярлык. Однако во многих ситуациях вы можете создать объект с помощью -init или +new и вручную отпустить его в соответствующее время.

7 голосов
/ 22 апреля 2011

Вот как все реализовано за сценой:

+(NSMutableArray*) new
{
    return [[NSMutableArray alloc] init];
}

и

+(NSMutableArray*) arrayWithCapacity:(NSNumber)capacity
{
    return [[NSMutableArray alloc] initWithCapacity:capacity] **autorelease**];
}

В первом случае массив выделяется только, и вы отвечаете за его удаление. Напротив, arrayWithCapacity автоматически освободил вас и не вызовет утечки, даже если вы забудете освободить.

4 голосов
/ 02 августа 2009

Какао использует определенные соглашения об именах. Все, что начинается с alloc, new или copy, возвращает что-то с retainCount, равным 1, и вы обязаны его освободить. Все остальное, что возвращает функция, имеет сбалансированное retainCount (оно может быть удержано чем-то другим, или оно может быть сохранено и освобождено).

Итак:

NSMutableArray *removals = [NSMutableArray new];

Имеет retainCount 1 и:

NSMutableArray *removals = [NSMutableArray arrayWithCapacity:99];

или

NSMutableArray *removals = [NSMutableArray array];

Нет, поскольку методы не имеют префикса alloc, new или copy. Все это прописано в документации по управлению памятью 1013 *. В частности:

Вы вступаете во владение объектом, если вы создать его с помощью метода, имя которого начинается с «alloc» или «new» или содержит «копию» (например, alloc, newObject или mutableCopy), или если вы отправить это сообщение. Вы ответственный за отказ владение объектами, которыми вы владеете, используя выпуск или авто-релиз. В любое другое время Вы получаете объект, вы не должны отпустите.

...