Как очистить NSMutableArray от пользовательских объектов без создания утечек памяти?

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

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

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

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

EDIT (после 4 ответов) - последний вопрос о разъяснении после больших ответов - я все еще не совсем уверен в переменных/свойствах экземпляра в своем пользовательском объекте, который я установил для сохранения? Кажется, что они выпущены только методом "dealloc" в моем классе пользовательских объектов, где мы делаем это вручную вместе с [super release].

Итак, если, очистка массива, если я делаю removeAllObjects, а затем NSArray выдает "выпуск" моим пользовательским объектам, но не вызывает "dealloc" , то как мои переменные экземпляра освобождаются?

Ответы

Ответ 1

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 вы можете просто освободить массив. Если массив освобожден, все, что внутри него освобождается, и если нет другой ссылки на объект, он будет освобожден.

Ответ 2

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

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

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

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

Ответ 3

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

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

Правила памяти 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 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!

Ответ 4

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

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