Unowned Reference (бесхозная ссылка) — это невладеющая ссылка в Swift, которая не увеличивает retain count объекта и, в отличие от weak, не устанавливается в nil после его освобождения. По данным Apple Swift Language Guide, 2026, unowned применяется когда гарантировано, что объект живёт как минимум столько же, сколько и ссылающийся на него объект. В отличие от Weak Reference, unowned не требует unwrap — это non-optional тип, что делает код чище, но возлагает на разработчика ответственность за гарантию времени жизни.
Главное
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.
В Swift unowned-ссылки объявляются ключевым словом unowned перед let или var. В отличие от weak, unowned может быть как let, так и var, и не требует опционального типа. Это свойство делает unowned удобным для ссылок, которые не могут быть nil по логике предметной области.
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 допускается, но встречается реже. Используется когда ссылка может быть заменена (например, перепривязка ребёнка к другому родителю). При замене освобождение старого объекта — ответственность внешнего владельца.
В Swift 5.0+ появилась поддержка unowned optional (unowned let x: Type?). Это компромисс: unowned гарантирует, что если ссылка не nil, то объект жив. Поведение при освобождении — crash, как и у обычного unowned.
Выбор между unowned и weak — одно из частых решений при проектировании Swift-архитектуры. Рассмотрим критерии и рекомендации для каждого случая.
| Критерий | Weak | Unowned |
|---|---|---|
| Optional | Да (Type?) | Нет (Type) |
| Zeroing при деаллокации | Авто в nil | Нет (риск висячего указателя) |
| Тип (let/var) | Только var | let или var |
| Производительность | Накладные расходы на weak-таблицу | Минимальные (простой указатель) |
| Безопасность | Безопасно (nil проверяется) | Риск EXC_BAD_ACCESS |
| Гарантия времени жизни | Не требуется | Требуется явная гарантия |
Используйте weak, если есть хотя бы малейшее сомнение во времени жизни объекта. Weak безопасен, понятен и не требует доказательств. Используйте unowned, только когда rule out все сценарии, при которых объект может освободиться раньше. Типичные случаи: ребёнок, не существующий без родителя; замыкание, выполняющееся синхронно; обращение к объекту в пределах его инициализатора.
По данным Airbnb Swift Style Guide, 2025, в больших кодовых базах рекомендуется использовать weak по умолчанию и unowned — только с явным комментарием, объясняющим гарантию времени жизни. Это снижает риск неочевидных крашей при рефакторинге.
Замыкания (closures) — второй по частоте сценарий использования unowned после parent-child отношений. Capture list [unowned self] применяется, когда self гарантированно живёт дольше замыкания. Рассмотрим правильные и неправильные сценарии.
Синхронные замыкания — sorted, filter, map. Они выполняются немедленно в текущем потоке, self точно жив. Capture list с unowned здесь допустим и даёт более чистый код.
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 }
}
Асинхронные замыкания — с задержкой, сетевыми запросами, анимациями. Self может освободиться между постановкой замыкания и его выполнением. Здесь unowned self → crash. Используйте [weak self].
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: почему эта ссылка безопасна и при каких условиях может нарушиться.
UIKit — зона повышенного риска для unowned. ViewController может быть освобождён в любой момент при навигации (pop, dismiss), выгрузке из памяти, смене ориентации. Если вы передаёте ViewController в замыкание с unowned self — при возврате из фона или завершении анимации self может быть nil.
Для снижения риска при использовании unowned следуйте этим правилам:
// Пример: документированная 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 и какие условия могут сломать гарантию.
Часто задаваемые вопросы
Runtime crash с EXC_BAD_ACCESS. Swift не проверяет валидность unowned-ссылки при доступе — это просто «сырой» указатель. Если объект освобождён, память перезаписана, и обращение к ней завершается аварийно. Это неотлавливаемое исключение (не try-catch).
Да, если протокол унаследован от AnyObject. Unowned работает со всеми reference-типами: классы, AnyObject-протоколы, Objective-C объекты. Value-типы (struct, enum) не поддерживают unowned, так как не участвуют в ARC.
Когда гарантия времени жизни абсолютна и очевидна — unowned безопаснее с точки зрения дизайна: не требует unwrap, не может быть nil, не маскирует ошибки. Если объект не может существовать без родителя, unowned делает это явным контрактом, а weak размывает гарантию.
Да: unowned быстрее, так как не требует доступа к weak-таблице runtime для zeroing. В большинстве приложений разница неощутима, но в высоконагруженных сценариях с миллионами обращений unowned может быть на 10–20% быстрее при чтении.
Рефакторинг — главная опасность unowned. Изменение времени жизни объекта (кэширование, асинхронные операции, переиспользование) может нарушить гарантию. Компилятор не предупредит. Решение: мигрировать на weak при изменении архитектуры или добавить комментарий-предупреждение.
Итоги
unowned let или unowned var; может быть non-optional и optional (Swift 5.0+)Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также