Ответ
Да, может. Это происходит при образовании цикла сильных ссылок (strong reference cycle или retain cycle), когда два или более объекта удерживают друг друга через strong ссылки. Автоматический подсчет ссылок (ARC) не может освободить такие объекты, так как счетчик каждого из них никогда не становится равным нулю.
Пример цикла:
class Person {
var apartment: Apartment? // Strong ссылка
}
class Apartment {
var tenant: Person? // Strong ссылка -> ЦИКЛ!
}
var john: Person? = Person()
var unit4A: Apartment? = Apartment()
john!.apartment = unit4A
unit4A!.tenant = john // Образовался цикл сильных ссылок
john = nil // Счетчик Person? = 1 (удерживается unit4A.tenant)
unit4A = nil // Счетчик Apartment? = 1 (удерживается john?.apartment)
// ОБА ОБЪЕКТА НИКОГДА НЕ ОСВОБОЖДАЮТСЯ -> УТЕЧКА ПАМЯТИ.
Решение: Разорвать цикл, заменив одну из сильных ссылок на weak или unowned.
class Apartment {
weak var tenant: Person? // Weak ссылка не увеличивает retain count
}
// Теперь при john = nil объект Person будет освобожден,
// после чего apartment?.tenant станет nil, и объект Apartment также освободится.
weak: Используется, когда ссылка может статьnil(опциональный тип).unowned: Используется, когда ссылка всегда должна иметь значение, но не должна удерживать объект (например, для родительских объектов).
Ответ 18+ 🔞
Ну ты представляешь, эта ваша умная система ARC, которая вроде как должна всё за собой подтирать, иногда тупит, как бревно. А всё почему? Потому что программисты, такие как мы с тобой, можем нагородить хуйни, из которой она сама не выберется. Это называется цикл сильных ссылок, или retain cycle, если по-умному.
Представь себе двух мудаков, которые держат друг друга за жопу так крепко, что никто не может разжать хватку. Каждый думает: «Раз он меня держит, то и я его не отпущу». И стоят они так до скончания времён, пока приложение не накроется медным тазом от нехватки памяти. Вот это и есть утечка.
Смотри, как это выглядит в коде, тут всё просто:
class Person {
var apartment: Apartment? // Сильная хватка за квартиру
}
class Apartment {
var tenant: Person? // Сильная хватка за жильца -> ЁПТА, ЦИКЛ!
}
var john: Person? = Person()
var unit4A: Apartment? = Apartment()
john!.apartment = unit4A // Джон хватает квартиру
unit4A!.tenant = john // Квартира хватает Джона. ВСЁ, ПИЗДЕЦ.
// Образовалась мёртвая хватка.
john = nil // Мы-то Джона отпустили, но на него всё ещё смотрит unit4A.tenant
unit4A = nil // И квартиру мы отпустили, но на неё смотрит john?.apartment
// Оба объекта теперь болтаются в памяти, как призраки, нахуй никому не нужные, но и не удаляемые. Классическая утечка.
Как разорвать этот порочный круг? Надо одного из этих кретинов заставить держаться слабо.
Используем weak — это как сказать: «Я на тебя ссылаюсь, но не держу тебя мертвой хваткой, живи своей жизнью, а я просто посмотрю». Когда объект умрёт, ссылка сама станет nil.
class Apartment {
weak var tenant: Person? // Слабая ссылка, не увеличивает счётчик
}
// Теперь, когда мы делаем john = nil, объект Person благополучно сдыхает.
// Ссылка apartment?.tenant автоматом становится nil.
// После этого и Apartment можно спокойно удалить. Порядок, блядь.
Есть ещё unowned — это для более суровых случаев. Это как сказать: «Тот, на кого я ссылаюсь, переживёт меня, или мы сдохнем вместе, но я его сильно держать не буду». Используй осторожно, ибо если объект таки умрёт, а ты попробуешь через unowned ссылку к нему обратиться — получишь краш приложения, и будешь потом ебаться с отладкой.