Unowned Reference (referencja nieposiadająca) — to nieposiadająca referencja w Swift, która nie zwiększa retain count obiektu i, w przeciwieństwie do weak, nie ustawia się na nil po jego zwolnieniu. Według Apple Swift Language Guide, 2026, unowned stosuje się, gdy mamy gwarancję, że obiekt żyje co najmniej tak długo, jak obiekt, który się do niego odwołuje. W przeciwieństwie do Weak Reference, unowned nie wymaga unwrap — to typ non-optional, co czyni kod czystszym, ale nakłada na programistę odpowiedzialność za gwarancję czasu życia.
Najważniejsze
Unowned Reference — to nieposiadająca referencja do obiektu w ARC, która nie zwiększa jego retain count. W przeciwieństwie do weak, unowned nie jest zerowana po deallocacji obiektu: nadal wskazuje na obszar pamięci, który został już zwolniony. Odwołanie do takiej referencji powoduje runtime crash z EXC_BAD_ACCESS.
Termin „nieposiadająca” odzwierciedla semantykę: obiekt istnieje, ale nikt nie odpowiada za jego czas życia. Programista jawnie deklaruje: „gwarantuję, że ten obiekt będzie żył, dopóki się do niego odwołuję”. Kompilator nie sprawdza tej gwarancji — to kontrakt na poziomie programisty.
Według Swift.org Documentation, 2026, unowned referencje są preferowane nad weak w scenariuszach z gwarantowanym czasem życia, ponieważ: nie wymagają typu opcjonalnego (czystszy kod), nie wymagają unwrap (mniej force-unwrap lub guard let) i nie mają narzutów na prowadzenie zeroing weak-tablicy. Jednak każde naruszenie kontraktu — crash.
W Swift unowned referencje deklaruje się słowem kluczowym unowned przed let lub var. W przeciwieństwie do weak, unowned może być zarówno let, jak i var i nie wymaga typu opcjonalnego. Ta właściwość czyni unowned wygodnym dla referencji, które nie mogą być nil zgodnie z logiką dziedziny.
class Country {
let name: String
var capital: City! // zostanie ustawione po inicjalizacji
init(name: String) { self.name = name }
}
class City {
let name: String
unowned let country: Country // ✅ unowned let — gwarancja życia
init(name: String, country: Country) {
self.name = name
self.country = country
}
}
// Użycie
let france = Country(name: "France")
let paris = City(name: "Paris", country: france)
france.capital = paris
// ✅ Country → City (strong), City → Country (unowned) — brak retain cycle
W tym przykładzie City unowned let country — miasto nie może istnieć bez kraju. Jeśli kraj zniknie, miasto (i referencja) stracą sens. Semantycznie to idealny przypadek dla unowned: gwarancja czasu życia istnieje, optional nie jest potrzebny, retain cycle nie powstaje.
unowned var jest dozwolone, ale występuje rzadziej. Używane, gdy referencja może zostać zastąpiona (np. przepięcie dziecka do innego rodzica). Przy zamianie zwolnienie starego obiektu — odpowiedzialność zewnętrznego właściciela.
W Swift 5.0+ pojawiło się wsparcie dla unowned optional (unowned let x: Type?). To kompromis: unowned gwarantuje, że jeśli referencja nie jest nil, to obiekt żyje. Zachowanie przy zwolnieniu — crash, jak w przypadku zwykłego unowned.
Wybór między unowned a weak — jedna z częstych decyzji przy projektowaniu architektury Swift. Rozważmy kryteria i zalecenia dla każdego przypadku.
| Kryterium | Weak | Unowned |
|---|---|---|
| Optional | Tak (Type?) | Nie (Type) |
| Zerowanie przy deallocacji | Auto na nil | Nie (ryzyko wiszącego wskaźnika) |
| Typ (let/var) | Tylko var | let lub var |
| Wydajność | Narzuty na weak-tablicę | Minimalne (zwykły wskaźnik) |
| Bezpieczeństwo | Bezpiecznie (nil sprawdzany) | Ryzyko EXC_BAD_ACCESS |
| Gwarancja czasu życia | Nie wymagana | Wymagana jawna gwarancja |
Używaj weak, jeśli istnieje choćby najmniejsza wątpliwość co do czasu życia obiektu. Weak jest bezpieczne, zrozumiałe i nie wymaga dowodów. Używaj unowned tylko wtedy, gdy wykluczysz wszystkie scenariusze, w których obiekt może zostać zwolniony wcześniej. Typowe przypadki: dziecko nieistniejące bez rodzica; domknięcie wykonujące się synchronicznie; odwołanie do obiektu w obrębie jego inicjalizatora.
Według Airbnb Swift Style Guide, 2025, w dużych bazach kodu zaleca się używanie weak domyślnie, a unowned — tylko z jawnym komentarzem wyjaśniającym gwarancję czasu życia. Zmniejsza to ryzyko nieoczywistych crashy przy refaktoryzacji.
Domknięcia (closures) — drugi co do częstości scenariusz użycia unowned po relacjach parent-child. Capture list [unowned self] stosuje się, gdy self gwarantowanie żyje dłużej niż domknięcie. Rozważmy prawidłowe i nieprawidłowe scenariusze.
Synchroniczne domknięcia — sorted, filter, map. Wykonują się natychmiast w bieżącym wątku, self na pewno żyje. Capture list z unowned jest tu dopuszczalny i daje czystszy kod.
class DataProcessor {
var items: [Int] = [3, 1, 4, 1, 5]
func processSorted() {
// ✅ unowned self — sorted wykonuje się synchronicznie, self gwarantowanie żyje
let sorted = items.sorted { [unowned self] a, b in
return self.customCompare(a, b)
}
}
func customCompare(_ a: Int, _ b: Int) -> Bool { return a < b }
}
Asynchroniczne domknięcia — z opóźnieniem, żądaniami sieciowymi, animacjami. Self może zostać zwolnione między umieszczeniem domknięcia a jego wykonaniem. Tu unowned self → crash. Używaj [weak self].
class NetworkLoader {
func loadData() {
// ❌ NIEBEZPIECZNE: unowned self w asynchronicznym domknięciu
URLSession.shared.dataTask(with: url) { [unowned self] data, _, _ in
self.handleResponse(data) // CRASH jeśli self został zwolniony
}.resume()
}
func handleResponse(_ data: Data?) { }
// ✅ PRAWIDŁOWO: weak self + guard
func loadDataSafe() {
URLSession.shared.dataTask(with: url) { [weak self] data, _, _ in
guard let self else { return }
self.handleResponse(data)
}.resume()
}
}
Zapamiętaj zasadę: unowned self — tylko dla synchronicznych domknięć wykonujących się natychmiast. Dla asynchronicznych — zawsze weak self + guard let. Wyjątek: jeśli jawnie przechowujesz referencję do obiektu do momentu zakończenia domknięcia (np. przez zachowanie silnego przechwycenia w innej zmiennej).
Unowned — potężne, ale niebezpieczne narzędzie. Rozważmy rzeczywiste scenariusze, w których unowned może prowadzić do crasha, oraz metody minimalizacji ryzyka.
Główne ryzyko unowned — zmiana logiki biznesowej, w wyniku której gwarancja czasu życia przestaje być spełniona. Programista refaktoryzuje kod: zmienia własność, wprowadza opóźnione zwalnianie, dodaje buforowanie — i unowned referencja zamienia się w bombę z opóźnionym zapłonem. Kompilator nie ostrzeże — tylko crash na urządzeniu użytkownika.
Zalecenie: używaj unowned tylko wtedy, gdy gwarancja czasu życia jest oczywista i udokumentowana. Dodawaj komentarz do każdego unowned: dlaczego ta referencja jest bezpieczna i w jakich warunkach może zostać naruszona.
UIKit — strefa podwyższonego ryzyka dla unowned. ViewController może zostać zwolniony w dowolnym momencie przy nawigacji (pop, dismiss), wyładowaniu z pamięci, zmianie orientacji. Jeśli przekazujesz ViewController do domknięcia z unowned self — przy powrocie z tła lub zakończeniu animacji self może być nil.
Aby zmniejszyć ryzyko przy używaniu unowned, przestrzegaj tych zasad:
// Przykład: udokumentowana unowned referencja z jawnym uzasadnieniem
class InvoiceLineItem {
let productName: String
let price: Decimal
// unowned Invoice — InvoiceLineItem nie może istnieć bez Invoice.
// Invoice tworzy Item i usuwa go przy swoim usunięciu.
// Gwarancja: Invoice żyje co najmniej tak długo, jak Item.
unowned let invoice: Invoice
init(productName: String, price: Decimal, invoice: Invoice) {
self.productName = productName
self.price = price
self.invoice = invoice
}
}
// To silna gwarancja: Invoice usuwa wszystkie Item w deinit.
// naruszenie gwarancji = błąd w logice biznesowej, który trzeba naprawić.
Dokumentowanie gwarancji — profesjonalny standard. W dużych projektach (Airbnb, Uber) code review wymaga uzasadnienia każdego unowned. Jeśli gwarancja nie jest oczywista — używaj weak. Komentarz do unowned pomaga przyszłym programistom zrozumieć, dlaczego tu nie ma weak i jakie warunki mogą złamać gwarancję.
Często zadawane pytania
Runtime crash z EXC_BAD_ACCESS. Swift nie sprawdza poprawności unowned referencji przy dostępie — to po prostu „surowy” wskaźnik. Jeśli obiekt został zwolniony, pamięć jest nadpisana, a odwołanie do niej kończy się awarią. To niełapalny wyjątek (nie try-catch).
Tak, jeśli protokół dziedziczy po AnyObject. Unowned działa ze wszystkimi typami referencyjnymi: klasy, protokoły AnyObject, obiekty Objective-C. Typy wartościowe (struct, enum) nie obsługują unowned, ponieważ nie uczestniczą w ARC.
Gdy gwarancja czasu życia jest absolutna i oczywista — unowned jest bezpieczniejsze z punktu widzenia projektu: nie wymaga unwrap, nie może być nil, nie maskuje błędów. Jeśli obiekt nie może istnieć bez rodzica, unowned czyni to jawnym kontraktem, a weak rozmywa gwarancję.
Tak: unowned jest szybsze, ponieważ nie wymaga dostępu do weak-tablicy runtime dla zerowania. W większości aplikacji różnica jest nieodczuwalna, ale w scenariuszach o wysokim obciążeniu z milionami odwołań unowned może być o 10–20% szybsze przy odczycie.
Refaktoring — główne zagrożenie dla unowned. Zmiana czasu życia obiektu (buforowanie, operacje asynchroniczne, ponowne użycie) może naruszyć gwarancję. Kompilator nie ostrzeże. Rozwiązanie: migruj na weak przy zmianie architektury lub dodaj komentarz ostrzegawczy.
Podsumowanie
unowned let lub unowned var; może być non-optional i optional (Swift 5.0+)Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również