В Objective-c Что происходит, если родитель освобождает дочернего элемента во время выполнения дочернего метода? - PullRequest
2 голосов
/ 08 февраля 2012

У меня есть родитель, который имеет сильную связь с ребенком. У ребенка слабая ссылка на родителя.

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

Что будет?

РЕДАКТИРОВАТЬ: я не использую ARC, и это будет работать на iOS 3.1.2

РЕДАКТИРОВАТЬ: Как насчет использования [NSValue valueWithNonretainedObject]?

В соответствии с этим: https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/GarbageCollection/Articles/gcAPI.html

Ссылка устанавливается на ноль, когда объект собирается мусором. Но поскольку в iOS нет сборки мусора, будет ли висящий указатель?

РЕДАКТИРОВАТЬ: Нашел этот полезный пост, поэтому я подумал, что поделюсь им с другими, узнавшими об этой теме тоже:

http://cocoawithlove.com/2009/07/rules-to-avoid-retain-cycles.html

Ответы [ 3 ]

3 голосов
/ 08 февраля 2012

Если вы используете что-то вроде:

[NSThread detachNewThreadSelector: selctor target: theChildObject withObject: nil];

у вас нет проблем. Цель сохраняется (и объект, если предоставляется), пока поток не завершит выполнение. Если нет, вам нужно организовать правильную синхронизацию, например, иметь атомный метод доступа для ребенка в родительском.

// in the parent interface

@property (retain) id child; // Note no nonatomic

// in the child thread 

id myChild = [parent child];  // myChild will be autoreleased in the current thread if the child property is atomic
if (myChild != nil)
{
    [myChild retain];             // not strictly necessary as long as the current autorelease pool is not drained 
    // do the stuff you need with myChild
    [myChild release];
}
1 голос
/ 08 февраля 2012

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

Однако быстрое исправление - это то, что сказал babbidi, или это, если вы используете ARC: сохраните сильную ссылку на родителя в локальной переменной, скажем, localParent, в начале метода. Это создает цикл сохранения и, следовательно, ни один из объектов не будет освобожден. Поскольку localParent является локальным, он автоматически освобождается в конце метода, прерывая цикл сохранения и позволяя освободить оба объекта.

Если ваш метод имеет возвращаемое значение, используйте локальный родительский элемент в асинхронном блоке для эффективной имитации поведения автоматического выпуска:

dispatch_async(dispatch_get_main_queue(), ^{
    [localParent self]; // this does nothing
});
0 голосов
/ 08 февраля 2012

Следующее не относится к ARC (в случае, если вы говорите об ARC)

Вероятно, произойдет сбой.Однако вы можете вызвать [self retain]; при запуске одного метода и [self release]; до его завершения (до возврата или до конца метода).Родитель вызвал бы release для дочернего элемента, но это было сохранено (если метод выполняется), поэтому retainCount все равно должно быть> 0 (когда вызывается dealloc).

Если метод использует слабую ссылку на родителя, то при освобождении родительский элемент должен вызвать child.parent = nil;, чтобы предотвратить доступ дочернего элемента к освобожденной памяти.

...