В каком случае обращение к `unowned` ссылке в Swift приведет к крашу приложения?

Ответ

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

Пример опасного сценария:

class Customer {
    let id: String
    var card: CreditCard?
    init(id: String) { self.id = id }
}

class CreditCard {
    let number: String
    unowned let customer: Customer // Карта не может существовать без Клиента
    init(number: String, customer: Customer) {
        self.number = number
        self.customer = customer
    }
}

var john: Customer? = Customer(id: "123") // 1. Создаем Customer
john!.card = CreditCard(number: "1111-2222", customer: john!) // 2. Создаем CreditCard

// 3. Customer деаллоцируется досрочно (например, покинул экран)
john = nil

// 4. Позже пытаемся получить доступ к карте (через другой код или глобальную ссылку)
// Если где-то сохранили ссылку на card, то вызов:
// print(card.customer.id) // Fatal error: Attempted to read an unowned reference but object was already deallocated

Почему это происходит:

  • unowned не является опциональной (unowned? не существует) и не обнуляется автоматически, в отличие от weak.
  • После john = nil экземпляр Customer уничтожается, но unowned customer внутри CreditCard продолжает указывать на освобожденную память.
  • Следующее обращение к этому месту в памяти вызывает краш.

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

Ответ 18+ 🔞

А, ну это же классика, блядь! Сейчас объясню на пальцах, а то некоторые думают, что unowned — это такая бесплатная weak без вопросительных знаков. Хуй там, плавали — знаем!

Смотри, unowned — это как дать другу в долг с криком «вернёшь, когда я попрошу, ты ж не мудак!». А потом этот друг взял и сдох, блядь. И кому теперь предъявлять-то? Никому. А ты стоишь с протянутой рукой и получаешь фатальную ошибку в ебало, потому что обращаешься в пустоту, в уже освобождённую память. Краш, пиздец, приложение упало.

Вот смотри на этот код, тут всё как в жизни:

class Customer {
    let id: String
    var card: CreditCard?
    init(id: String) { self.id = id }
}

class CreditCard {
    let number: String
    unowned let customer: Customer // Карта не может существовать без Клиента
    init(number: String, customer: Customer) {
        self.number = number
        self.customer = customer
    }
}

Всё вроде логично, да? Карта без клиента — хуйня, а не карта. Завязываем её unowned ссылкой на клиента. И в теории-то да, клиент должен жить дольше. Но жизнь, она, сука, сложнее.

Дальше разыгрывается трагедия в трёх актах, блядь:

  1. Появился Джон: var john: Customer? = Customer(id: "123"). Живой, здоровый, в памяти.
  2. Дали Джону карту: john!.card = CreditCard(number: "1111-2222", customer: john!). Карта теперь знает своего хозяина через эту самую unowned ссылку. Всё ок.
  3. А вот и пиздец: john = nil. Джона — нет. Деаллоцировался, освободил память. А его карта-то где? А хуй её знает! Может, где-то в другом месте её сохранили. Но внутри неё customer — это теперь указатель в никуда, в освобождённый кусок памяти. Это как адрес дома, который уже снесли.
  4. Финальный аккорд: Пытаемся где-то потом прочитать card.customer.id. И тут — BANG! Fatal error: Attempted to read an unowned reference but object was already deallocated. В рот мне чих-пых, приложение накрылось медным тазом.

Вот в чём, блядь, соль! unowned — она не опциональная, её в nil не превратить. Она либо указывает на живой объект, либо ведёт прямиком в краш. weak — та хоть сама в nil превратится и даст тебе спокойно проверить, а unowned — нет, она максималистка, ёпта.

Так когда её, эту стерву, использовать-то? Да только тогда, когда ты на 146% уверен, что объект, на который ссылаешься, переживёт тебя. Классика — это делегат, который является твоим родительским контроллером. Контроллер живёт, пока на экране вьюха, вьюха держит ссылку на делегата-контроллер через unowned. Когда контроллер с экрана уйдёт — и вьюху вместе с собой прихватит, так что обращений после смерти не будет.

А во всех остальных случаях, если есть хоть тень сомнения — бери weak, не еби себе и другим мозг. Безопасность, блядь, прежде всего!