Как управлять unsafe_unretained ivars / properties? - PullRequest
3 голосов
/ 30 января 2012

Я запустил target-c и iOS пару недель назад (стоит иметь в виду), и заранее прошу прощения за ужасную диаграмму !!

enter image description here

Диаграмма выше показывает структуру моих звонков в веб-сервис. Тонкие стрелки обозначают объект, создающий другой объект, тогда как толстые стрелки обозначают объект, содержащий сильную (сохраненную) ссылку на объект, на который указывает указатель.

Я считаю, что это содержит так называемую "круговую ссылку" и создаст проблемы, когда дело доходит до освобождения объектов.

Я понимаю, что простым ответом было бы заменить некоторые сильные ссылки на слабые, что я хотел бы сделать, за исключением того, что мой проект также ориентирован на iOS 3.2 (не мое решение - я не могу изменить это факт!). Итак, я считаю правильным сказать, что вместо этого я должен использовать __unsafe_unretained, но я весьма обеспокоен тем фактом, что они не будут автоматически обнуляться, так как у меня возникнут проблемы EXC_BAD_ACCESS, когда объекты будут освобождены. ..

Итак, моя проблема, во-первых, в том, что у меня есть круговые ссылки. Чтобы решить, я должен был бы использовать __unsafe_unretained, что приводит ко второй моей проблеме: как правильно управлять этим?

Вопрос, который может быть связан: как NSURLConnection управляет своими сильными ссылками? Я слышал из разных источников, что он сохраняет свой делегат? Итак ... если я сохраню NSURLConnection (и я также являюсь его делегатом), и он сохранит меня, это также будет круговая ссылка, нет? Как это обойти мою проблему?

Любой совет очень приветствуется!

С уважением, Ник

Ответы [ 2 ]

6 голосов
/ 30 января 2012

Когда родитель имеет ссылку на дочерний объект, он должен использовать строгую ссылку. Когда у дочернего элемента есть ссылка на его родительский объект, ему следует использовать слабую ссылку, называемую unsafe_unretained.

По соглашению отношения делегатов в iOS обычно являются слабыми ссылками, поэтому вы обнаружите, что большинство свойств делегатов в собственных классах Apple объявлены как unsafe_unretained.

Таким образом, ваш контроллер сохраняет сервисы, которые он использует, но сервисы слабо связаны с контроллером. Таким образом, если контроллер освобожден, вся партия может быть безопасно удалена без каких-либо циклических ссылок.

Опасность в этом заключается в том, что если веб-служба выполняет какую-то длительную задачу, а контроллер освобождается до его завершения, служба остается с висящим указателем на свой теперь освобожденный делегат. Если он попытается отправить сообщение делегату, например «Я закончил», произойдет сбой.

Есть несколько подходов, которые помогут решить эту проблему (они не являются взаимоисключающими - вы должны попытаться сделать их все, когда это возможно):

1) Всегда устанавливайте свойства делегатов ваших служб равными nil в методе dealloc вашего контроллера. Это гарантирует, что когда контроллер освобождается, ссылки на делегаты на него устанавливаются на ноль (своего рода грубый, ручной эквивалент того, что слабые ссылки ARC делают автоматически).

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

3) Старайтесь не иметь долгосрочных служб, принадлежащих чему-то временному, например контроллеру представления. Подумайте о создании одноэлементных объектов (экземпляров общих статических объектов), которые владеют вашими сервисами, чтобы сервис мог выполнять свою работу в фоновом режиме независимо от того, что происходит в слое представления. Контроллер по-прежнему может вызывать службу, но не владеет ею - служба принадлежит статическому объекту, который будет существовать в течение всего времени работы приложения, поэтому риск утечек или преждевременных выпусков отсутствует. Служба может связываться с контроллером через NSNotifications вместо вызовов делегатов, поэтому нет необходимости иметь ссылку на объект, который может исчезнуть. NSNotifications - отличный способ взаимодействия между несколькими классами без создания циклических ссылок.

1 голос
/ 30 января 2012

Все ваши вопросы и проблемы верны, и эта проблема с предыдущим использованием assign (теперь лучше именуемым __unsafe_unretained) является причиной, по которой Apple разработала автоматическое обнуление для weak. Но мы достаточно безопасно справились с assign делегатами на протяжении многих лет, поэтому, как вы подозреваете, есть способы сделать это.

Во-первых, на практике вы всегда должны очистить себя в качестве делегата, когда вы выпускаете объект, для которого вы были делегированы. До ARC, это традиционно делалось в dealloc:

- (void)dealloc {
  [tableView_ setDelegate:nil];
  [tableView_ release];
  tableView_ = nil;
}

Вы все равно должны включить это setDelegate:nil в свой dealloc, если delegate равно __unsafe_unretained. Это решит наиболее распространенную форму проблемы (когда делегат освобождается перед делегирующим объектом).

Что касается NSURLConnection, вы также правы, что он сохраняет свой делегат. Это нормально, потому что срок его службы обычно намного короче, чем у его делегата (по сравнению с делегатом табличного представления, который почти всегда имеет тот же срок жизни, что и табличное представление). См. " Как обойти / обработать ошибки делегирования EXC_BAD_ACCESS? Obj C " для более подробного обсуждения этого в контексте, предшествующем ARC (те же концепции применимы в новом мире сильных / слабых).

...