← Назад к вопросам

Где используется unowned?

2.0 Middle🔥 171 комментариев
#Управление памятью

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI5 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Использование unowned в Swift

unowned — это модификатор слабой ссылки в Swift, который используется для разрешения проблем циклических ссылок (retain cycles) в ситуациях, когда время жизни одного объекта гарантированно не превышает время жизни другого.

Основное отличие от weak

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

Типичные сценарии использования unowned

1. Родитель-ребёнок в иерархии объектов

Когда дочерний объект не должен переживать родительский:

class Customer {
    let name: String
    var card: CreditCard?
    
    init(name: String) {
        self.name = name
    }
    
    deinit { print("Customer \(name) освобождён") }
}

class CreditCard {
    let number: String
    unowned let customer: Customer // Карта не существует без клиента
    
    init(number: String, customer: Customer) {
        self.number = number
        self.customer = customer
    }
    
    deinit { print("Card #\(number) освобождена") }
}

// Использование
var john: Customer? = Customer(name: "John")
john!.card = CreditCard(number: "1234", customer: john!)
john = nil // Оба объекта освобождаются, нет retain cycle

2. Замыкания (closures) с гарантированным временем жизни

Когда замыкание захватывает self, но гарантировано, что self будет существовать:

class DataLoader {
    var data: String = "Данные"
    lazy var dataProcessor: () -> String = { [unowned self] in
        // Предполагаем, что DataLoader существует при вызове
        return "Обработка: \(self.data)"
    }
    
    func process() {
        print(dataProcessor())
    }
    
    deinit { print("DataLoader освобождён") }
}

3. Протоколы с ограничением класса (class-bound protocols)

В сочетании с протоколами, которые могут соблюдаться только классами:

protocol DataSourceDelegate: AnyObject {
    func didReceiveData(_ data: String)
}

class DataManager {
    unowned let delegate: DataSourceDelegate // Не владеем делегатом
    
    init(delegate: DataSourceDelegate) {
        self.delegate = delegate
    }
    
    func loadData() {
        delegate.didReceiveData("Данные загружены")
    }
}

Ключевые правила и рекомендации

Когда использовать unowned:

  • Когда время жизни объектов строго связано — один объект не может существовать без другого
  • В иерархиях Parent-Child, где родитель владеет детьми
  • Для делегатов и обратных вызовов, где объект-получатель гарантировано существует
  • Когда нужно избежать опциональности ссылки, но при этом предотвратить retain cycle

Когда НЕ использовать unowned:

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

Опасности неправильного использования

class DangerousExample {
    var closure: (() -> Void)?
    
    func setupClosure() {
        closure = { [unowned self] in
            print(self) // Криш, если self уже освобождён!
        }
    }
    
    deinit { print("DangerousExample освобождён") }
}

// Опасный сценарий:
var obj: DangerousExample? = DangerousExample()
obj?.setupClosure()
obj = nil
// Вызов closure теперь приведёт к крешу

Альтернативы и безопасные паттерны

  1. [weak self] с guard — более безопасный подход:
closure = { [weak self] in
    guard let strongSelf = self else { return }
    // Работаем с strongSelf
}
  1. [unowned(unsafe)] — эквивалентно Objective-C __unsafe_unretained. Только для случаев, когда вы полностью контролируете время жизни объектов и готовы к неопределённому поведению при ошибках.

Вывод

unowned — это инструмент для опытных разработчиков, который требует глубокого понимания модели памяти Swift и времени жизни объектов. Его правильное использование приводит к более чистой и эффективной кодовой базе (без избыточных опционалов), но ошибки в его применении фатальны. В сомнительных случаях всегда предпочитайте weak с последующей проверкой, даже если это требует дополнительного кода.