Как очистить NSMutableArray пользовательских объектов без создания утечек памяти? - PullRequest
15 голосов
/ 29 сентября 2011

Если у меня есть NSMutableArray пользовательских объектов, как я могу легко очистить массив, не вызывая проблем с памятью? Предположим, что в классе пользовательского объекта есть метод dealloc, который правильно выдал переменные экземпляра и т. Д.

Например, можно ли использовать метод NSAray "removeAllObjects"?

  • Если да - как это работает - вызывает ли метод "removeAllObjects" метод "dealloc" для каждого объекта при его удалении

  • Если нет - какой самый простой подход для использования?

РЕДАКТИРОВАТЬ (после 4 ответов) - Последний вопрос прояснения после великих ответов - я все еще не совсем уверен насчет переменных / свойств экземпляра в моем пользовательском объекте, который я установил для сохранения? Похоже, что они выпускаются только с помощью метода "dealloc" в моем классе пользовательских объектов, где мы делаем это вручную вместе с [super release].

Так что, если при повторной очистке массива я выполняю removeAllObjects, а затем NSArray выдает «release» моим пользовательским объектам, но не вызывает «dealloc», то как освободятся переменные моего экземпляра?

Ответы [ 4 ]

33 голосов
/ 29 сентября 2011

removeAllObjects удалит объект из массива. Этот процесс отправит сообщение об освобождении объекту, и это уменьшит его счетчик ссылок. Когда счетчик ссылок достигнет нуля, объект будет освобожден.

не делай так, потому что это протечет.

NSObject *object = [[NSObject alloc] init];       + 1
[array addObject:object];                         + 1
[array removeAllObjects];                         - 1
                                                =======
                                                = + 1 -> Leak

это правильный путь:

NSObject *object = [[[NSObject alloc] init] autorelease]; + 1 (from alloc) - 1 (from autorelease)
[array addObject:object];                         + 1
[array removeAllObjects];                         - 1
                                                =======
                                                =   0 -> Object will be deallocated

Вместо вызова removeAllObjects вы можете просто освободить массив. Если массив освобожден, все, что находится внутри него, освобождается, и если нет другой ссылки на объект, он будет освобожден.

3 голосов
/ 29 сентября 2011

Да, просто позвоните removeAllObjects. Просто чтобы быть уверенным, вы не вызываете retain, когда добавляете объект в массив или создаете массив с объектами. Это делается для вас автоматически.

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

Единственное, что вам нужно иметь в dealloc - это сам объект массива. То есть, предполагая, что это переменная экземпляра или ivar?

Чтобы проверить, все ли хорошо, запустите Analyzer, используя Product -> Analyze. А затем дайте приложению профиль в разделе «Инструменты» с помощью инструмента «Утечки», чтобы убедиться, что ни один из ваших кодов не вызывает утечек памяти.

1 голос
/ 29 сентября 2011

В основном метод removeAllObjects отправляет сообщение release всем объектам. Метод release уменьшает количество ссылок на объекты . И если счетчик ссылок объекта достигает 0, тогда объекту будет отправлено сообщение dealloc.

Ответ на ваш вопрос: [array removeAllObjects] совершенно безопасен. Кстати, если вам больше не нужен массив, вы можете напрямую вызвать [array release], который освобождает все его объекты, а также массив.

1 голос
/ 29 сентября 2011

Метод dealloc никогда не вызывается напрямую. Все делается с помощью механизма retain / release (и принципа подсчета ссылок). Так что вызывается метод release, а не dealloc напрямую. Метод dealloc вызывается средой выполнения только в том случае, если последний вызов release приводит к тому, что счетчик ссылок (retainCount) объекта достигает нуля, что означает, что объект действительно освобожден из памяти, поскольку никто его больше не использует.

NSArray и все контейнерные классы в Какао (NSDictionary, NSSet, ...) сохраняют свои значения. Поэтому, когда вы добавляете объект в контейнер, такой как NSArray, он будет retain это значение. И когда вы удаляете это значение (в том числе при вызове removeAllObjects "), оно будет release it.

Правилам Memory Mgmt легко следовать, но единственное правило, которое имеет значение, это то, что вам нужно вызывать release или autorelease, только если вы вызывали alloc, retain или copy методы. Это всегда ответственность объекта, который сделал alloc / retain / copy для вызова release / autorelease. Никогда не оставляйте alloc / retain / copy без ожидающего release / autorelease звонка, чтобы сбалансировать его (или у вас будут утечки), но с другой стороны, никогда не звоните release / autorelease если вы не сделали alloc / retain / copy, позвоните себе.

Хороший пример 1:

MyClass* obj = [[MyClass alloc] init]; // here you do an alloc
[myArray addObject:obj]; // the NSArray "myArray" retains the object obj
// so now you can release it, the array has the responsability of the object while it is held in the array
[obj release]; // this release balance the "alloc" on the first line, so that's good

[myArray removeAllObjects]; // there the object held by the array receive a release while being removed from the array. As nobody retains it anymore, its dealloc method will be called automatically.

Хороший пример 2:

MyClass* obj = [[MyClass alloc] init]; // here you do an alloc
[myArray addObject:obj]; // the NSArray "myArray" retains the object obj
// so now you can release it, the array has the responsability of the object while it is held in the array
[myArray removeAllObjects]; // there the object held by the array receive a release while being removed from the array. But your own code still retains a reference to it (because of the "alloc" on first line) so it won't be removed from memory right now
[obj release]; // this release balance the "alloc" on the first line, and as nobody retains the object anymore, its dealloc method will be called and it will be deallocated from memory

Хороший пример 3:

MyClass* obj = [self getSomeObjectFromAnotherMethod]; // here you don't have an "alloc" on this line
[myArray addObject:obj]; // the array retains the object
[myArray removeAllObjects]; // the array release the object while it removes it from the array
// no need to call "release" here as there is no "alloc" done in the scope of this code

Плохой пример:

MyClass* obj = [self getSomeObjectFromAnotherMethod]; // here you don't have an "alloc" on this line
[myArray addObject:obj]; // the array retains the object
[myArray removeAllObjects]; // the array release the object while it removes it from the array
[obj release]; // Crash here! obj does not exists anymore and has been deallocated from memory before this line!
...