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 тільки коли виключені всі сценарії, за яких об’єкт може звільнитися раніше. Типові випадки: дитина, яка не існує без батька; замикання, що виконується синхронно; звернення до об’єкта в межах його ініціалізатора.
За даними 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. Виняток: якщо явно зберігаєте посилання на об’єкт до завершення замикання (наприклад, через збереження сильного захоплення в іншій змінній).
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 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.
Читайте також