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

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

Ризики 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
  • Гарантія — потребує явного 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 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

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