Вот интересная проблема. У меня есть контроллер представления, давайте назовем его MyViewController. Одним из его членов является пользовательское представление, назовем это MyCustomView. MyCustomView также имеет ссылку на своего родителя, MyViewController, поскольку MyViewController используется в качестве делегата для пользовательского представления. Проблема возникает, когда MyViewController выгружен, скажем, из-за предупреждения памяти. Вот что происходит:
Во-первых, viewDidUnload вызывается для MyViewController. Это выглядит примерно так:
- (void)viewDidUnload {
[super viewDidUnload];
self.myCustomView = nil;
self.someData = nil;
...
}
Когда выполняется self.myCustomView = nil, это вызывает освобождение myCustomView. Процедура Deloc для MyCustomView выглядит примерно так:
- (void)dealloc {
...
[delegate release];
...
[super dealloc];
}
Напомним, что делегатом является MyViewController. Если бы MyCustomView были выпущены первыми, это не было бы проблемой, поскольку счетчик ссылок для MyViewController был бы больше 1, но в этом случае MyViewController уже не имеет других ссылок на него. Это приводит к тому, что MyViewController освобождается, а именно вызывается его процедура dealloc, которая выглядит следующим образом:
- (void)dealloc {
[myCustomView release];
[somedata release];
...
[super dealloc];
}
Как вы можете видеть, члены MyViewController, а именно "somedata", получают выпуск ДО завершения процедуры viewDidUnload для MyViewController. Когда подпрограммы dealloc для MyViewController и MyCustomView завершены, и мы возвращаемся к завершению подпрограммы viewDidUnload, мы переходим к строке
self.somedata = nil;
Теперь некоторые данные не равны нулю, но их значение уже выпущено! Это вызывает исключение.
Кажется, это критический недостаток в подсчете ссылок. Как вы справляетесь с двусторонними ссылками в подобных объектах, которые вызывают освобождение друг друга?
Один из ответов - всегда устанавливать нулевые члены в подпрограмме dealloc. Мне не нравится этот ответ. Это много дополнительной работы и не должно быть необходимо большую часть времени. Другой ответ заключается в изменении порядка viewDidUnload, чтобы в конце всегда происходило освобождение дочерних объектов, которые могут иметь обратные указатели. Мне тоже не нравится это решение, и иногда оно может даже не работать.
Как обойти эту проблему?