Циклы удержания возникают, когда два или более объекта имеют сильные ссылки друг на друга, создавая "замкнутый круг". Вследствие этого, счетчик ссылок каждого объекта никогда не достигает нуля, и он не может быть освобожден ARC (Automatic Reference Counting). Это приводит к утечкам памяти, так как объекты остаются в памяти даже после того, как они перестают использоваться.
Пример:
swift
В этом примере, если Person имеет сильную ссылку на Apartment, а Apartment имеет сильную ссылку на Person, то при попытке освободить эти объекты они не будут удалены, так как их счетчики ссылок не достигнут нуля.
Как решить проблему циклов удержания:
Используйте слабые (weak) или бесхозные (unowned) ссылки.
weak: Используется, когда возможно, что связанный объект станет nil в течение жизненного цикла другого объекта. Ссылка становится nil автоматически, когда объект, на который она ссылается, освобождается. Является опционалом.unowned: Используется, когда известно, что связанный объект имеет тот же или более длительный жизненный цикл, чем объект, имеющий на него ссылку. Не может быть nil.Исправленный пример с использованием weak:
Изменим ссылку tenant в классе Apartment на слабую:
swift
Теперь, когда Person объект освобождается, ссылка tenant в Apartment автоматически становится nil, разрывая цикл удержания и позволяя Apartment также быть освобожденным.
Таблица сравнения weak и unowned:
| Характеристика | weak | unowned |
|---|---|---|
| Тип ссылки | Опциональный | Неопциональный |
| Может быть nil? | Да, при освобождении объекта | Нет |
| Когда использовать | Когда объект может быть nil | Когда объект не может быть nil |
| Производительность | Немного медленнее из-за проверки на nil | Слегка быстрее |
Также циклы удержания могут возникать в замыканиях.
Когда замыкание захватывает instance properties или self, оно может создать сильную ссылку на объект. Если этот объект также имеет сильную ссылку на замыкание (например, через свойство-замыкание или делегата), возникает цикл удержания.
Как решить проблему циклов удержания в замыканиях:
Используйте список захвата (capture list).
swift
В данном примере [weak self] создает слабую ссылку на self внутри замыкания, предотвращая цикл удержания.