Программирование SwiftOptionals и система типовРазработчик приложений для iOS

После уничтожения объекта обращение к unowned ссылке завершается чем и почему?

После уничтожения объекта обращение к unowned-ссылке завершается чем и почему?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

Обращение к unowned-ссылке после уничтожения объекта приводит к аварийному завершению программы во время выполнения. В отличие от weak, такая ссылка не становится nil, потому что объявляется как не-Optional и предполагает, что объект гарантированно живёт дольше ссылки.

Исторический контекст

В Swift слабые ссылки нужны для разрыва циклов владения, но у них есть цена: значение всегда Optional, поэтому каждое обращение требует проверки или извлечения. unowned предназначен для случаев, когда жизненный цикл объектов связан известным инвариантом и ссылка не должна владеть объектом.

Такой подход позволяет выразить в системе типов намерение «ссылка не владеет объектом, но объект гарантированно существует при обращении». Если это предположение нарушается, Swift не скрывает ошибку преобразованием в nil, а немедленно сигнализирует о нарушении инварианта.

Постановка проблемы

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

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

Подробное решение

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

Минимальный пример:

final class Customer { let name: String init(name: String) { self.name = name } } final class Receipt { unowned let customer: Customer init(customer: Customer) { self.customer = customer } } var receipt: Receipt? do { let customer = Customer(name: "Анна") receipt = Receipt(customer: customer) } _ = receipt!.customer.name // аварийное завершение

weak в аналогичной ситуации имел бы тип Customer? и после уничтожения объекта стал бы nil. Поэтому weak выбирают, когда отсутствие объекта является допустимым состоянием; unowned — когда отсутствие означает ошибку проектирования или нарушение жизненного цикла.

unowned не является способом управления памятью и не делает объект бессмертным. Он лишь задаёт невладеющую ссылку с более сильным контрактом, чем у weak; этот контракт контролируется во время выполнения, а не гарантируется компилятором полностью.

Ситуация из практики

Представим экран заказа и его модель. Модель владеет экраном через сильную ссылку, а экран обращается к модели только пока сам существует. Если гарантируется, что экран не переживёт модель, обратная ссылка может быть unowned.

Вариант с weak надёжнее при неопределённом жизненном цикле: модель может исчезнуть, а экран проверит nil. Минус — Optional-логика и необходимость явно обрабатывать отсутствие модели.

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

Что кандидаты часто упускают

  1. Чем unowned отличается от обычной сильной ссылки?

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

  2. Почему unowned нельзя считать просто невладеющим аналогом Optional-ссылки?

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

  3. Когда unowned может быть менее подходящим, чем weak, даже если сейчас жизненный цикл кажется очевидным?

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