Какой тип ссылки на объект используется по умолчанию в Swift?

Ответ

По умолчанию все ссылки на экземпляры классов в Swift являются сильными (strong).

Что это значит: Сильная ссылка увеличивает счетчик ссылок (ARC) объекта. Пока существует хотя бы одна сильная ссылка, объект не освобождается из памяти.

Пример и проблема:

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, ни unit4A никогда не освободятся.

john = nil
unit4A = nil // Память не освобождается из-за retain cycle.

Решение: Для разрыва циклов удержания используются:

  • weak (слабая ссылка): Не увеличивает счетчик ссылок. Автоматически становится nil при освобождении объекта. Используется, когда ссылка может пережить объект.
  • unowned (невладеющая ссылка): Предполагает, что объект существует на протяжении всей жизни ссылки. Не становится nil. При обращении к освобожденному unowned объекту произойдет краш. Используется, когда время жизни объектов связано.

Ответ 18+ 🔞

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

Смотри, как это работает, ёпта. В Свифте, если ты просто объявил ссылку, она по умолчанию — сильная. Это значит, что объект, на который она указывает, будет жить, пока на него хоть кто-то так сильно смотрит. Вроде логично, да? А теперь представь классическую ситуацию: есть Person (Человек) и Apartment (Квартира).

Человек может иметь квартиру, а квартира может иметь жильца. И вот ты, такой довольный, создаёшь Джона и квартиру 4А.

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 = nil

А нихуя! Память не освободилась. Они оба ещё в памяти сидят, потому что john.apartment всё ещё держит unit4A, а unit4A.tenant всё ещё держит john. Они как два алкаша, которые друг друга из лужи поднять не могут, но и отпустить боятся. Мёртвая хватка, пиздец.

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

  1. weak (слабая ссылка). Это как твой знакомый, который говорит: «Я тебя поддержу!», но как только дела становятся хуёвыми — он сразу nil. Ссылка не увеличивает счётчик удержаний. Идеально, когда один объект может пережить другой. В нашем примере, квартира может существовать без жильца, значит ссылку tenant делаем слабой.

  2. unowned (невладеющая ссылка). Это уже более жёсткая история. Это как твой родственник, который говорит: «Ты точно не умрёшь раньше меня, я в тебя верю!». Ссылка тоже не увеличивает счётчик, но она НЕ становится nil. Если объект, на который она ссылается, всё-таки освободится, а ты попробуешь к нему обратиться — привет, краш приложения, в рот меня чих-пых! Используется, когда время жизни объектов жёстко связано. Например, кредит не может существовать без клиента, который его взял.

Короче, суть в чём: чтобы не было этих ебаных циклов, где объекты держат друг друга в памяти навечно, одну из ссылок в паре нужно объявить как weak или unowned. Тогда, когда основной объект (например, john) удалят, слабая ссылка из квартиры на него автоматически обнулится, и всё почистится как надо. Волнение ебать, но работает.