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 μόνο όταν έχετε αποκλείσει όλα τα σενάρια στα οποία το αντικείμενο θα μπορούσε να απελευθερωθεί νωρίτερα. Τυπικές περιπτώσεις: παιδί που δεν υπάρχει χωρίς γονέα· closure που εκτελείται σύγχρονα· αναφορά σε αντικείμενο εντός του init του.
Σύμφωνα με το Airbnb Swift Style Guide, 2025, σε μεγάλες βάσεις κώδικα συνιστάται η χρήση weak από προεπιλογή και unowned — μόνο με ρητό σχόλιο που εξηγεί την εγγύηση διάρκειας ζωής. Αυτό μειώνει τον κίνδυνο μη προφανών crash κατά την αναδιάρθρωση.
Τα closures — το δεύτερο συχνότερο σενάριο χρήσης του unowned μετά τις σχέσεις parent-child. Το capture list [unowned self] εφαρμόζεται όταν το self είναι εγγυημένο ότι ζει περισσότερο από το closure. Ας εξετάσουμε τα σωστά και λάθος σενάρια.
Σύγχρονα closures — 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 }
}
Ασύγχρονα closures — με καθυστέρηση, αιτήματα δικτύου, κινούμενα σχέδια. Το self μπορεί να απελευθερωθεί μεταξύ της τοποθέτησης του closure και της εκτέλεσής του. Εδώ unowned self → crash. Χρησιμοποιήστε [weak self].
class NetworkLoader {
func loadData() {
// ΕΠΙΚΙΝΔΥΝΟ: unowned self σε ασύγχρονο closure
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 — μόνο για σύγχρονα closures που εκτελούνται αμέσως. Για ασύγχρονα — πάντα weak self + guard let. Εξαίρεση: αν κρατάτε ρητά μια αναφορά στο αντικείμενο μέχρι την ολοκλήρωση του closure (π.χ. αποθηκεύοντας ένα ισχυρό capture σε άλλη μεταβλητή).
Το unowned — ένα ισχυρό αλλά επικίνδυνο εργαλείο. Ας εξετάσουμε τα πραγματικά σενάρια όπου το unowned μπορεί να οδηγήσει σε crash και τις μεθόδους ελαχιστοποίησης κινδύνου.
Ο κύριος κίνδυνος του unowned — αλλαγή της επιχειρηματικής λογικής κατά την οποία η εγγύηση διάρκειας ζωής παύει να ισχύει. Ο προγραμματιστής αναδιαρθρώνει τον κώδικα: αλλάζει την ιδιοκτησία, εισάγει καθυστερημένη απελευθέρωση, προσθέτει προσωρινή αποθήκευση — και η unowned αναφορά μετατρέπεται σε ωρολογιακή βόμβα. Ο μεταγλωττιστής δεν θα προειδοποιήσει — μόνο crash στη συσκευή του χρήστη.
Σύσταση: χρησιμοποιήστε unowned μόνο όταν η εγγύηση διάρκειας ζωής είναι προφανής και τεκμηριωμένη. Προσθέστε ένα σχόλιο σε κάθε unowned: γιατί αυτή η αναφορά είναι ασφαλής και υπό ποιες συνθήκες μπορεί να παραβιαστεί.
UIKit — ζώνη αυξημένου κινδύνου για unowned. Το ViewController μπορεί να απελευθερωθεί ανά πάσα στιγμή κατά την πλοήγηση (pop, dismiss), εκφόρτωση από μνήμη, αλλαγή προσανατολισμού. Εάν μεταβιβάσετε το ViewController σε ένα closure με 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 αναφοράς κατά την πρόσβαση — είναι απλώς ένας "ακατέργαστος" δείκτης. Εάν το αντικείμενο έχει απελευθερωθεί, η μνήμη έχει αντικατασταθεί και η πρόσβαση σε αυτήν καταλήγει σε crash. Αυτή είναι μια μη αναχαιτίσιμη εξαίρεση (όχι 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. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.
Διαβάστε επίσης