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) |
| Нулиране при деалокация | Автоматично на 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. Изключение: ако изрично държите референция към обекта до завършване на затварянето (напр. чрез запазване на силен capture в друга променлива).
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 работи с всички референтни типове: класове, AnyObject протоколи, Objective-C обекти. Типовете стойности (struct, enum) не поддържат unowned, защото не участват в ARC.
Когато гаранцията за живот е абсолютна и очевидна — unowned е по-безопасен от гледна точка на дизайна: не изисква unwrap, не може да бъде nil, не маскира грешки. Ако обектът не може да съществува без родител, unowned го прави изричен договор, а weak замъглява гаранцията.
Да: unowned е по-бърз, защото не изисква достъп до weak-таблицата на runtime за нулиране. В повечето приложения разликата е незабележима, но във високонатоварени сценарии с милиони достъпи, unowned може да бъде с 10–20% по-бърз при четене.
Рефакторирането — основната опасност за unowned. Промяната на живота на обекта (кеширане, асинхронни операции, повторно използване) може да наруши гаранцията. Компилаторът няма да предупреди. Решение: мигрирайте към weak при промяна на архитектурата или добавете предупредителен коментар.
Резюме
unowned let или unowned var; може да бъде non-optional и optional (Swift 5.0+)Ще разработим мобилно приложение под ключ
IT Sectr създава iOS и Android приложения за стартъпи и бизнеси от 2017 г. Ще ви консултираме и ще предложим най-доброто решение.
Прочетете също