Unowned Reference (referință fără proprietar) — este o referință care nu deține în Swift, care nu mărește retain count al obiectului și, spre deosebire de weak, nu se setează la nil după eliberarea acestuia. Conform Apple Swift Language Guide, 2026, unowned se aplică atunci când este garantat că obiectul trăiește cel puțin cât obiectul care se referă la el. Spre deosebire de Weak Reference, unowned nu necesită unwrap — este un tip non-optional, ceea ce face codul mai curat, dar pune responsabilitatea garanției duratei de viață asupra dezvoltatorului.
Puncte principale
Unowned Reference — este o referință care nu deține către un obiect în ARC, care nu mărește retain count-ul acestuia. Spre deosebire de weak, unowned nu este zeroizată după deallocarea obiectului: continuă să indice spre zona de memorie care a fost deja eliberată. Accesarea unei astfel de referințe cauzează runtime crash cu EXC_BAD_ACCESS.
Termenul „fără proprietar“ reflectă semantica: obiectul există, dar nimeni nu răspunde pentru durata sa de viață. Dezvoltatorul declară explicit: „garantez că acest obiect va trăi cât timp mă refer la el“. Compilatorul nu verifică această garanție — este un contract la nivelul dezvoltatorului.
Conform Swift.org Documentation, 2026, referințele unowned sunt preferate față de weak în scenariile cu durata de viață garantată deoarece: nu necesită un tip opțional (cod mai curat), nu necesită unwrap (mai puțin force-unwrap sau guard let) și nu au overhead pentru întreținerea tabelei weak zeroing. Cu toate acestea, orice încălcare a contractului — crash.
În Swift, referințele unowned se declară cu cuvântul cheie unowned înainte de let sau var. Spre deosebire de weak, unowned poate fi atât let, cât și var și nu necesită un tip opțional. Această proprietate face unowned convenabil pentru referințele care nu pot fi nil conform logicii domeniului.
class Country {
let name: String
var capital: City! // va fi setat după inițializare
init(name: String) { self.name = name }
}
class City {
let name: String
unowned let country: Country // ✅ unowned let — garanție de viață
init(name: String, country: Country) {
self.name = name
self.country = country
}
}
// Utilizare
let france = Country(name: "France")
let paris = City(name: "Paris", country: france)
france.capital = paris
// ✅ Country → City (strong), City → Country (unowned) — fără retain cycle
În acest exemplu City unowned let country — orașul nu poate exista fără țară. Dacă țara dispare, orașul (și referința) își pierd sensul. Semantic, acesta este un caz ideal pentru unowned: garanția duratei de viață există, optional nu este necesar, retain cycle nu apare.
unowned var este permis, dar apare mai rar. Se folosește când referința poate fi înlocuită (de exemplu, reatașarea unui copil la un alt părinte). La înlocuire, eliberarea obiectului vechi — responsabilitatea proprietarului extern.
În Swift 5.0+ a apărut suportul pentru unowned optional (unowned let x: Type?). Este un compromis: unowned garantează că dacă referința nu este nil, obiectul este viu. Comportamentul la eliberare — crash, ca și în cazul unowned obișnuit.
Alegerea între unowned și weak — una dintre deciziile frecvente la proiectarea arhitecturii Swift. Să examinăm criteriile și recomandările pentru fiecare caz.
| Criteriu | Weak | Unowned |
|---|---|---|
| Optional | Da (Type?) | Nu (Type) |
| Zeroing la deallocare | Auto la nil | Nu (risc de pointer agățat) |
| Tip (let/var) | Doar var | let sau var |
| Performanță | Costuri pentru weak-tabel | Minime (pointer simplu) |
| Siguranță | Sigur (nil verificat) | Risc EXC_BAD_ACCESS |
| Garanția duratei de viață | Nu este necesară | Necesită garanție explicită |
Folosește weak dacă există măcar cea mai mică îndoială cu privire la durata de viață a obiectului. Weak este sigur, de înțeles și nu necesită dovezi. Folosește unowned doar când ai exclus toate scenariile în care obiectul s-ar putea elibera mai devreme. Cazuri tipice: copil care nu există fără părinte; closure care se execută sincron; referirea la un obiect în cadrul inițializatorului său.
Conform Airbnb Swift Style Guide, 2025, în bazele de cod mari se recomandă utilizarea weak implicit și unowned — doar cu un comentariu explicit care explică garanția duratei de viață. Aceasta reduce riscul de crashuri neevidente la refactorizare.
Closure-urile — al doilea scenariu ca frecvență de utilizare a unowned după relațiile parent-child. Capture list [unowned self] se aplică atunci când self garantează că trăiește mai mult decât closure-ul. Să examinăm scenariile corecte și incorecte.
Closure-uri sincrone — sorted, filter, map. Se execută imediat în threadul curent, self este cu siguranță viu. Capture list cu unowned este acceptabil aici și oferă cod mai curat.
class DataProcessor {
var items: [Int] = [3, 1, 4, 1, 5]
func processSorted() {
// ✅ unowned self — sorted se execută sincron, self garantat viu
let sorted = items.sorted { [unowned self] a, b in
return self.customCompare(a, b)
}
}
func customCompare(_ a: Int, _ b: Int) -> Bool { return a < b }
}
Closure-uri asincrone — cu întârziere, cereri de rețea, animații. Self poate fi eliberat între plasarea closure-ului și executarea sa. Aici unowned self → crash. Folosește [weak self].
class NetworkLoader {
func loadData() {
// ❌ PERICULOS: unowned self în closure asincron
URLSession.shared.dataTask(with: url) { [unowned self] data, _, _ in
self.handleResponse(data) // CRASH dacă self este eliberat
}.resume()
}
func handleResponse(_ data: Data?) { }
// ✅ CORECT: weak self + guard
func loadDataSafe() {
URLSession.shared.dataTask(with: url) { [weak self] data, _, _ in
guard let self else { return }
self.handleResponse(data)
}.resume()
}
}
Țe minte regula: unowned self — doar pentru closure-uri sincrone care se execută imediat. Pentru asincrone — întotdeauna weak self + guard let. Excepție: dacă păstrezi explicit o referință la obiect până la terminarea closure-ului (de exemplu, prin păstrarea unui capture puternic într-o altă variabilă).
Unowned — un instrument puternic, dar periculos. Să examinăm scenariile reale în care unowned poate duce la crash și metodele de minimizare a riscului.
Riscul principal al unowned — modificarea logicii de business prin care garanția duratei de viață încetează să fie respectată. Dezvoltatorul refactorizează codul: schimbă proprietatea, introduce eliberarea întârziată, adaugă cache — și referința unowned se transformă într-o bombă cu ceas. Compilatorul nu va avertiza — doar crash pe dispozitivul utilizatorului.
Recomandare: folosește unowned doar când garanția duratei de viață este evidentă și documentată. Adaugă un comentariu la fiecare unowned: de ce această referință este sigură și în ce condiții poate fi încălcată.
UIKit — zonă de risc ridicat pentru unowned. ViewController poate fi eliberat în orice moment la navigare (pop, dismiss), descărcare din memorie, schimbare de orientare. Dacă transmiți ViewController-ul într-un closure cu unowned self — la revenirea din fundal sau la terminarea animației self poate fi nil.
Pentru a reduce riscul la utilizarea unowned, urmează aceste reguli:
// Exemplu: referință unowned documentată cu justificare explicită
class InvoiceLineItem {
let productName: String
let price: Decimal
// unowned Invoice — InvoiceLineItem nu poate exista fără Invoice.
// Invoice creează Item și îl șterge la ștergerea sa.
// Garanție: Invoice trăiește cel puțin cât Item.
unowned let invoice: Invoice
init(productName: String, price: Decimal, invoice: Invoice) {
self.productName = productName
self.price = price
self.invoice = invoice
}
}
// Aceasta este o garanție puternică: Invoice șterge toate Item în deinit.
// încălcarea garanției = bug în logica de business care trebuie reparat.
Documentarea garanțiilor — standard profesional. În proiectele mari (Airbnb, Uber) code review necesită justificarea fiecărui unowned. Dacă garanția nu este evidentă — folosește weak. Comentariul la unowned ajută viitorii dezvoltatori să înțeleagă de ce aici nu este weak și ce condiții pot rupe garanția.
Întrebări frecvente
Runtime crash cu EXC_BAD_ACCESS. Swift nu verifică validitatea referinței unowned la acces — este doar un pointer „brut“. Dacă obiectul a fost eliberat, memoria este suprascrisă, iar accesarea ei se termină cu avarie. Aceasta este o excepție neacoperită (nu try-catch).
Da, dacă protocolul moștenește din AnyObject. Unowned funcționează cu toate tipurile de referință: clase, protocoale AnyObject, obiecte Objective-C. Tipurile de valoare (struct, enum) nu suportă unowned deoarece nu participă la ARC.
Când garanția duratei de viață este absolută și evidentă — unowned este mai sigur din punct de vedere al designului: nu necesită unwrap, nu poate fi nil, nu maschează erorile. Dacă obiectul nu poate exista fără părinte, unowned face acest lucru un contract explicit, iar weak estompează garanția.
Da: unowned este mai rapid, deoarece nu necesită acces la tabela weak runtime pentru zeroing. În majoritatea aplicațiilor diferența este insesizabilă, dar în scenariile cu încărcare ridicată cu milioane de accesări, unowned poate fi cu 10–20% mai rapid la citire.
Refactorizarea — principalul pericol pentru unowned. Modificarea duratei de viață a obiectului (cache, operații asincrone, reutilizare) poate încălca garanția. Compilatorul nu va avertiza. Soluția: migrează la weak la schimbarea arhitecturii sau adaugă un comentariu de avertizare.
Rezumat
unowned let sau unowned var; poate fi non-optional și optional (Swift 5.0+)Vom dezvolta o aplicație mobilă la cheie
IT Sectr creează aplicații iOS și Android pentru startup-uri și afaceri din 2017. Vă vom consilia și vă vom propune cea mai bună soluție.
Citiți și