Unowned Reference: что это такое, синтаксис и применение в мобильных приложениях

Автор: IT Sectr Опубликовано: 2026-03-30 Время чтения: 9 мин

Unowned Reference (бесхозная ссылка) — это невладеющая ссылка в Swift, которая не увеличивает retain count объекта и, в отличие от weak, не устанавливается в nil после его освобождения. По данным Apple Swift Language Guide, 2026, unowned применяется когда гарантировано, что объект живёт как минимум столько же, сколько и ссылающийся на него объект. В отличие от Weak Reference, unowned не требует unwrap — это non-optional тип, что делает код чище, но возлагает на разработчика ответственность за гарантию времени жизни.

Главное

  • Unowned Reference — невладеющая ссылка без автоматического обнуления; non-optional, не увеличивает retain count
  • Гарантия — применяется когда объект гарантированно не может быть освобождён раньше ссылающегося
  • Отличие от weak — unowned не nil-обнуляется (риск crash), weak обнуляется (безопасно)
  • Сценарии — parent-child с гарантией жизни, замыкания с unowned self, синглтоны и Service Locator
  • Риск — обращение к освобождённому unowned объекту вызывает runtime crash (EXC_BAD_ACCESS)

Что такое Unowned Reference?

Unowned Reference — это невладеющая ссылка на объект в ARC, которая не увеличивает его retain count. В отличие от weak, unowned-ссылка не обнуляется после деаллокации объекта: она продолжает указывать на область памяти, которая уже освобождена. Обращение к такой ссылке вызывает runtime crash с EXC_BAD_ACCESS.

Термин «бесхозная» отражает семантику: объект существует, но никто не отвечает за его время жизни. Разработчик явно заявляет: «я гарантирую, что этот объект будет жив, пока я на него ссылаюсь». Компилятор не проверяет эту гарантию — это контракт на уровне разработчика.

По данным Swift.org Documentation, 2026, unowned-ссылки предпочтительнее weak в сценариях с гарантированным временем жизни, так как они: не требуют опционального типа (код чище), не требуют unwrap (меньше force-unwrap или guard let), и не имеют накладных расходов на ведение zeroing weak-таблицы. Однако любое нарушение контракта — crash.

Синтаксис unowned в Swift

В Swift unowned-ссылки объявляются ключевым словом unowned перед let или var. В отличие от weak, unowned может быть как let, так и var, и не требует опционального типа. Это свойство делает unowned удобным для ссылок, которые не могут быть nil по логике предметной области.

swift
class Country {
    let name: String
    var capital: City!           // будет установлено после инициализации
    init(name: String) { self.name = name }
}

class City {
    let name: String
    unowned let country: Country   // ✅ unowned let — гарантия жизни

    init(name: String, country: Country) {
        self.name = name
        self.country = country
    }
}

// Использование
let france = Country(name: "France")
let paris = City(name: "Paris", country: france)
france.capital = paris
// ✅ Country → City (strong), City → Country (unowned) — нет retain cycle

В этом примере City unowned let country — город не может существовать без страны. Если страна исчезнет, город (и ссылка) потеряют смысл. Семантически это идеальный случай для unowned: гарантия времени жизни есть, optional не нужен, retain cycle не возникает.

unowned var

unowned var допускается, но встречается реже. Используется когда ссылка может быть заменена (например, перепривязка ребёнка к другому родителю). При замене освобождение старого объекта — ответственность внешнего владельца.

Unowned Optional

В Swift 5.0+ появилась поддержка unowned optional (unowned let x: Type?). Это компромисс: unowned гарантирует, что если ссылка не nil, то объект жив. Поведение при освобождении — crash, как и у обычного unowned.

Unowned vs Weak: когда использовать что

Выбор между unowned и weak — одно из частых решений при проектировании Swift-архитектуры. Рассмотрим критерии и рекомендации для каждого случая.

КритерийWeakUnowned
OptionalДа (Type?)Нет (Type)
Zeroing при деаллокацииАвто в nilНет (риск висячего указателя)
Тип (let/var)Только varlet или var
ПроизводительностьНакладные расходы на weak-таблицуМинимальные (простой указатель)
БезопасностьБезопасно (nil проверяется)Риск EXC_BAD_ACCESS
Гарантия времени жизниНе требуетсяТребуется явная гарантия

Практическое правило

Используйте weak, если есть хотя бы малейшее сомнение во времени жизни объекта. Weak безопасен, понятен и не требует доказательств. Используйте unowned, только когда rule out все сценарии, при которых объект может освободиться раньше. Типичные случаи: ребёнок, не существующий без родителя; замыкание, выполняющееся синхронно; обращение к объекту в пределах его инициализатора.

По данным Airbnb Swift Style Guide, 2025, в больших кодовых базах рекомендуется использовать weak по умолчанию и unowned — только с явным комментарием, объясняющим гарантию времени жизни. Это снижает риск неочевидных крашей при рефакторинге.

Unowned self в замыканиях

Замыкания (closures) — второй по частоте сценарий использования unowned после parent-child отношений. Capture list [unowned self] применяется, когда self гарантированно живёт дольше замыкания. Рассмотрим правильные и неправильные сценарии.

Когда unowned self безопасен

Синхронные замыкания — sorted, filter, map. Они выполняются немедленно в текущем потоке, self точно жив. Capture list с unowned здесь допустим и даёт более чистый код.

swift
class DataProcessor {
    var items: [Int] = [3, 1, 4, 1, 5]

    func processSorted() {
        // ✅ unowned self — sorted выполняется синхронно, self гарантированно жив
        let sorted = items.sorted { [unowned self] a, b in
            return self.customCompare(a, b)
        }
    }

    func customCompare(_ a: Int, _ b: Int) -> Bool { return a < b }
}

Когда unowned self опасен

Асинхронные замыкания — с задержкой, сетевыми запросами, анимациями. Self может освободиться между постановкой замыкания и его выполнением. Здесь unowned self → crash. Используйте [weak self].

swift
class NetworkLoader {
    func loadData() {
        // ❌ ОПАСНО: unowned self в асинхронном замыкании
        URLSession.shared.dataTask(with: url) { [unowned self] data, _, _ in
            self.handleResponse(data)  // CRASH если self освобождён
        }.resume()
    }

    func handleResponse(_ data: Data?) { }

    // ✅ ПРАВИЛЬНО: weak self + guard
    func loadDataSafe() {
        URLSession.shared.dataTask(with: url) { [weak self] data, _, _ in
            guard let self else { return }
            self.handleResponse(data)
        }.resume()
    }
}

Запоминайте правило: unowned self — только для синхронных замыканий, выполняющихся немедленно. Для асинхронных — всегда weak self + guard let. Исключение: if явно сохраняете ссылку на объект до завершения замыкания (например, через сохранение сильного захвата в другой переменной).

Риски unowned и как их избежать

Unowned — мощный, но опасный инструмент. Рассмотрим реальные сценарии, где unowned может привести к крашу, и методы минимизации риска.

Рефакторинг и изменение гарантий

Главный риск unowned — изменение бизнес-логики, при котором гарантия времени жизни перестаёт выполняться. Разработчик рефакторит код: меняет владение, вводит отложенное освобождение, добавляет кэширование — и unowned-ссылка превращается в бомбу замедленного действия. Компилятор не предупредит — только краш на устройстве пользователя.

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

Unowned в иерархиях UIKit

UIKit — зона повышенного риска для unowned. ViewController может быть освобождён в любой момент при навигации (pop, dismiss), выгрузке из памяти, смене ориентации. Если вы передаёте ViewController в замыкание с unowned self — при возврате из фона или завершении анимации self может быть nil.

Лучшие практики

Для снижения риска при использовании unowned следуйте этим правилам:

  • Предпочитайте weak по умолчанию — weak безопасен, unowned — оптимизация, а не стандарт
  • Документируйте гарантии — для каждого unowned пишите комментарий с обоснованием
  • Избегайте unowned в ViewController — UIKit жизненный цикл непредсказуем для unowned-гарантий
  • Используйте unowned только для синхронных замыканий — sorted, filter, map — безопасные кандидаты
  • Проверяйте при code review — каждый unowned требует обоснования от автора кода
  • Мигрируйте на weak при малейших сомнениях — проигрыш в читаемости (один guard let) меньше, чем crash в продакшне
swift
// Пример: документированная unowned-ссылка с явным обоснованием
class InvoiceLineItem {
    let productName: String
    let price: Decimal

    // unowned Invoice — InvoiceLineItem не может существовать без Invoice.
    // Invoice создаёт Item и удаляет его при своём удалении.
    // Гарантия: Invoice живёт как минимум столько же, сколько Item.
    unowned let invoice: Invoice

    init(productName: String, price: Decimal, invoice: Invoice) {
        self.productName = productName
        self.price = price
        self.invoice = invoice
    }
}

// Это сильная гарантия: Invoice удаляет все Item в deinit.
// нарушение гарантии = баг в бизнес-логике, который нужно фиксить.

Документирование гарантий — профессиональный стандарт. В крупных проектах (Airbnb, Uber) code review требует обоснования каждого unowned. Если гарантия неочевидна — используйте weak. Комментарий к unowned помогает будущим разработчикам понять, почему здесь не weak и какие условия могут сломать гарантию.

Часто задаваемые вопросы

Что произойдет при обращении к unowned ссылке после освобождения объекта?

Runtime crash с EXC_BAD_ACCESS. Swift не проверяет валидность unowned-ссылки при доступе — это просто «сырой» указатель. Если объект освобождён, память перезаписана, и обращение к ней завершается аварийно. Это неотлавливаемое исключение (не try-catch).

Можно ли использовать unowned с протоколами?

Да, если протокол унаследован от AnyObject. Unowned работает со всеми reference-типами: классы, AnyObject-протоколы, Objective-C объекты. Value-типы (struct, enum) не поддерживают unowned, так как не участвуют в ARC.

Когда unowned безопаснее weak?

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

Есть ли разница в производительности между unowned и weak?

Да: unowned быстрее, так как не требует доступа к weak-таблице runtime для zeroing. В большинстве приложений разница неощутима, но в высоконагруженных сценариях с миллионами обращений unowned может быть на 10–20% быстрее при чтении.

Как refactoring влияет на unowned-гарантии?

Рефакторинг — главная опасность unowned. Изменение времени жизни объекта (кэширование, асинхронные операции, переиспользование) может нарушить гарантию. Компилятор не предупредит. Решение: мигрировать на weak при изменении архитектуры или добавить комментарий-предупреждение.

Итоги

  • Unowned Reference — невладеющая ссылка без zeroing; non-optional, не увеличивает retain count
  • Гарантия — requires явное proof, что объект живёт не меньше ссылающегося на него кода
  • Синтаксисunowned let или unowned var; может быть non-optional и optional (Swift 5.0+)
  • Unowned vs Weak — unowned быстрее и чище, но weak безопаснее; weak — выбор по умолчанию
  • Замыкания — unowned self только для синхронных замыканий; асинхронные требуют [weak self]
  • Документирование — каждый unowned должен иметь комментарий с обоснованием гарантии
  • Рекомендация — при сомнении выбирайте weak; unowned — для явных и задокументированных контрактов

Мы разработаем мобильное приложение под ключ

IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.

Обсудить проект

Читайте также