Weak Reference — τι είναι, σύνταξη και εφαρμογή στην ανάπτυξη εφαρμογών για κινητά

Συγγραφέας: IT Sectr Δημοσιεύτηκε: 2026-03-30 Χρόνος ανάγνωσης: 9 λεπ

Weak Reference (αδύναμη αναφορά) — είναι μια αναφορά σε ένα αντικείμενο που δεν αυξάνει τον μετρητή συγκράτησής του στο ARC. Σύμφωνα με τον Apple Swift Language Guide, 2026, οι weak αναφορές δηλώνονται με τη λέξη-κλειδί weak και έχουν πάντα προαιρετικό τύπο. Όταν το αντικείμενο ελευθερώνεται, όλες οι weak αναφορές σε αυτό ορίζονται αυτόματα σε nil, αποτρέποντας τους αιωρούμενους δείκτες και καθιστώντας τις αδύναμες αναφορές έναν ασφαλή μηχανισμό για τη διάσπαση του retain cycle.

Κύρια

  • Weak Reference — αναφορά που δεν επηρεάζει το retain count του αντικειμένου; κατά την απελευθέρωση του αντικειμένου μηδενίζεται
  • Δήλωση — λέξη-κλειδί weak πριν από var; τύπος πάντα προαιρετικός (?)
  • Εφαρμογή — εκπρόσωποι (delegates), closures, σχέσεις parent-child για διάσπαση retain cycle
  • Ασφάλεια — αυτόματη ρύθμιση σε nil μετά την αποδέσμευση του αντικειμένου (zeroing weak)
  • Διαφορά από unowned — weak μηδενίζεται και είναι ασφαλές, unowned δεν μηδενίζεται και απαιτεί εγγύηση ζωής

Τι είναι το Weak Reference;

Weak Reference — είναι μια μη κατέχουσα αναφορά σε ένα αντικείμενο στο ARC (Automatic Reference Counting). Σε αντίθεση με την strong αναφορά, η οποία αυξάνει το retain count του αντικειμένου και εγγυάται τη ζωή του, η weak αναφορά επιτρέπει στο αντικείμενο να ελευθερωθεί ακόμα κι αν υπάρχουν ακόμα αναφορές σε αυτό. Μετά την απελευθέρωση, η weak αναφορά ορίζεται αυτόματα σε nil — αυτό ονομάζεται zeroing weak.

Zeroing weak — βασικό χαρακτηριστικό του runtime της Swift και Objective-C. Όταν ο μετρητής αναφορών του αντικειμένου φτάσει στο μηδέν και το αντικείμενο αποδεσμευτεί, το runtime διατρέχει όλες τις weak αναφορές σε αυτό το αντικείμενο (αποθηκευμένες σε έναν ειδικό weak πίνακα) και τις ορίζει σε nil. Αυτό εγγυάται ότι η πρόσβαση στην ελευθερωμένη μνήμη (use-after-free) είναι αδύνατη μέσω weak αναφορών — οποιαδήποτε ανάγνωση επιστρέφει nil.

Σύμφωνα με το Apple WWDC 2012 Session 406, οι zeroing weak αναφορές εξάλειψαν μια ολόκληρη κατηγορία σφαλμάτων crash που σχετίζονταν με αιωρούμενους δείκτες (dangling pointers), τα οποία ήταν διαδεδομένα στη χειροκίνητη διαχείριση μνήμης (MRR). Στο MRR οι αδύναμες αναφορές υπήρχαν μόνο ως __unsafe_unretained — δεν μηδενίζονταν και η πρόσβαση σε ένα ελευθερωμένο αντικείμενο οδηγούσε σε EXC_BAD_ACCESS.

Σύνταξη weak σε Swift και Objective-C

Ας εξετάσουμε τη σύνταξη δήλωσης weak αναφορών και στις δύο γλώσσες του οικοσυστήματος Apple. Παρά το κοινό runtime, η σύνταξη διαφέρει, αλλά η σημασιολογία είναι ταυτόσημη.

Swift

Στη Swift οι weak αναφορές δηλώνονται με τη λέξη-κλειδί weak πριν από var. Ο τύπος πρέπει να είναι πάντα προαιρετικός (Type?), καθώς η αναφορά μπορεί να μηδενιστεί ανά πάσα στιγμή. Οι σταθερές (let) δεν μπορούν να είναι weak — μόνο μεταβλητές.

swift
class ViewController: UIViewController {
    // weak-ιδιότητες: μόνο var, μόνο optional
    weak var delegate: ViewControllerDelegate?
    weak var parentView: UIView?

    weak var completionHandler: ((Bool) -> Void)?  // ⚠️ closures δεν αποθηκεύουν weak
    // ⬆️ Σφάλμα: weak μπορεί να εφαρμοστεί μόνο σε class-types, όχι σε closure
}

Σημαντικό: το weak εφαρμόζεται μόνο σε στιγμιότυπα κλάσεων (class-types), AnyObject και πρωτόκολλα που κληρονομούν από AnyObject. Struct, enum και closure δεν μπορούν να είναι weak — είναι τύποι τιμής και δεν συμμετέχουν στο ARC.

Objective-C

Στο Objective-C οι weak ιδιότητες δηλώνονται μέσω του χαρακτηριστικού __weak ή του τροποποιητή weak στο property:

objective-c
// Objective-C: weak property
@interface MyViewController : UIViewController
@property (weak, nonatomic) id<MyDelegate> delegate;
@end

// Τοπική weak-μεταβλητή
__weak MyObject *weakRef = someStrongObject;

Το Objective-C runtime παρέχει επίσης zeroing weak, αλλά επιπλέον αποκλείει τη χρήση weak με C-δομές και ορισμένα αντικείμενα Core Foundation. Για αυτά χρησιμοποιείται __unsafe_unretained — χωρίς zeroing.

Πότε να χρησιμοποιείτε αδύναμες αναφορές

Οι αδύναμες αναφορές — δεν είναι καθολική λύση, αλλά εργαλείο για συγκεκριμένα σενάρια. Η χρήση weak παντού οδηγεί σε υπερβολική πολυπλοκότητα και υποβαθμίζει την αναγνωσιμότητα. Ας εξετάσουμε τα σωστά σενάρια εφαρμογής.

Εκπρόσωποι (Delegate pattern)

Εκπρόσωπος (Delegate) — το κύριο σενάριο για weak. Το αντικείμενο-ιδιοκτήτης (π.χ. UITableView) κρατά strong αναφορά στον εαυτό του, και ο εκπρόσωπος (UIViewController) δεν πρέπει να κατέχει τον πίνακα. Το Apple SDK εγγυάται ότι όλοι οι delegate και dataSource είναι weak. Για τα δικά σας πρωτόκολλα χρησιμοποιείτε πάντα weak var delegate.

Parent-Child με ανάδραση

Όταν το θυγατρικό αντικείμενο πρέπει να αναφέρεται στο γονικό (π.χ. ChildViewController για πρόσβαση στον συντονιστή), χρησιμοποιήστε weak αναφορά. Ο γονέας κατέχει το παιδί (strong), το παιδί παρατηρεί τον γονέα (weak) — retain cycle αποκλείεται.

Ασύγχρονα closures

Capture list [weak self] — ο τυπικός τρόπος αποφυγής retain cycle σε closures που αποθηκεύονται ως ιδιότητες κλάσης. Εάν το self μπορεί να ελευθερωθεί πριν την ολοκλήρωση του closure — weak self είναι υποχρεωτικό.

ΣενάριοWeakStrong
Delegate✅ Πάντα weak❌ Retain cycle
Parent → Child❌ Δεν χρειάζεται (ο γονέας πρέπει να κατέχει)✅ Strong
Child → Parent✅ Weak❌ Retain cycle
Ασύγχρονο callback✅ [weak self]❌ Κίνδυνος retain cycle
Ισχυρή σύνδεση (owned)❌ unowned✅ Strong

Γενικός κανόνας: εάν το αντικείμενο A κατέχει το B (A → B strong), τότε το B → A πρέπει να είναι weak ή unowned. Η κατεύθυνση των strong αναφορών πρέπει πάντα να είναι από τον ιδιοκτήτη προς τον υφιστάμενο.

Weak vs Unowned: σύγκριση και σενάρια

Τόσο το weak όσο και το unowned δεν αυξάνουν το retain count, αλλά διαφέρουν στη συμπεριφορά μετά την αποδέσμευση του αντικειμένου. Η επιλογή μεταξύ τους είναι θέμα εγγυήσεων ζωής.

Διαφορές

Weak: μηδενίζεται αυτόματα (nil), τύπος πάντα optional, απαιτεί unwrap πριν από τη χρήση. Ασφαλές — η πρόσβαση σε nil δεν προκαλεί crash.

Unowned: δεν μηδενίζεται, τύπος non-optional. Εάν το αντικείμενο έχει ελευθερωθεί, η unowned αναφορά γίνεται αιωρούμενος δείκτης — η πρόσβαση σε αυτήν προκαλεί runtime crash. Το unowned υποθέτει ότι το αντικείμενο ζει όχι λιγότερο από το μέρος που αναφέρεται σε αυτό.

Πότε να επιλέξετε weak

Weak επιλέξτε εάν: το αντικείμενο μπορεί να αποδεσμευτεί ανά πάσα στιγμή (εκπρόσωπος μετά το κλείσιμο οθόνης), δεν ελέγχετε τη διάρκεια ζωής του αντικειμένου ή αμφιβάλλετε για τις εγγυήσεις. Weak — καθολική ασφαλής επιλογή.

Πότε να επιλέξετε unowned

Unowned επιλέξτε εάν: το αντικείμενο είναι εγγυημένο ότι δεν μπορεί να ελευθερωθεί νωρίτερα από το αναφερόμενο (π.χ. Customer → CreditCard, όπου η κάρτα δεν υπάρχει χωρίς πελάτη). Το unowned δίνει non-optional API χωρίς unwrap, το οποίο είναι πιο βολικό στον κώδικα.

swift
class Order {
    let id: Int
    var items: [Item] = []

    init(id: Int) { self.id = id }

    // Ισχυρή σύνδεση: Order κατέχει το Item
    func addItem(name: String) {
        let item = Item(name: name, order: self)
        items.append(item)
    }
}

class Item {
    let name: String
    unowned let order: Order          // ✅ unowned — Item δεν ζει χωρίς Order

    init(name: String, order: Order) {
        self.name = name
        self.order = order
    }
}

// Παράδειγμα με weak: εκπρόσωπος χωρίς εγγύηση ζωής
protocol NetworkServiceDelegate: AnyObject {
    func didReceiveResponse(data: Data)
}

class NetworkService {
    weak var delegate: NetworkServiceDelegate?  // ✅ weak — εκπρόσωπος μπορεί να φύγει
}

Στο παράδειγμα, το Item χρησιμοποιεί unowned, επειδή το στοιχείο παραγγελίας δεν μπορεί να υπάρξει χωρίς την ίδια την παραγγελία — η εγγύηση ζωής είναι σιδερένια. Το NetworkService χρησιμοποιεί weak, επειδή ο εκπρόσωπος (π.χ. ViewController) μπορεί να κλείσει και να ελευθερωθεί ανά πάσα στιγμή.

Περιορισμοί weak αναφορών και παγίδες

Οι αδύναμες αναφορές — ισχυρό εργαλείο, αλλά έχουν περιορισμούς που είναι σημαντικό να κατανοήσετε για σωστή εφαρμογή στην ανάπτυξη iOS.

Απόδοση weak

Οι αδύναμες αναφορές είναι πιο αργές από τις strong: σε κάθε πρόσβαση το runtime ελέγχει εάν το αντικείμενο έχει ελευθερωθεί (lookup στον weak πίνακα). Στη συντριπτική πλειονότητα των σεναρίων η διαφορά είναι αόρατη, αλλά σε καυτούς βρόχους με εκατομμύρια προσβάσεις το weak μπορεί να είναι σημείο συμφόρησης. Για σενάρια υψηλού φόρτου χρησιμοποιήστε strong και αναδιοργανώστε την αρχιτεκτονική.

Weak δεν εφαρμόζεται σε τύπους τιμής

Struct, enum, tuple — τύποι τιμής που δεν συμμετέχουν στο ARC. Η προσπάθεια δήλωσης weak struct οδηγεί σε σφάλμα μεταγλώττισης. Για αποθήκευση αδύναμης αναφοράς σε τύπο τιμής χρησιμοποιήστε wrapper σε class-type ή closure.

Weak σε πολυνηματικό περιβάλλον

Zeroing weak είναι thread-safe: εάν το αντικείμενο ελευθερώνεται σε ένα νήμα, η weak αναφορά μηδενίζεται σε όλα τα νήματα ατομικά. Ωστόσο, το διάστημα μεταξύ ανάγνωσης της weak αναφοράς και χρήσης της μπορεί να οδηγήσει σε συνθήκη ανταγωνισμού — το αντικείμενο ελευθερώνεται μεταξύ λήψης της weak αναφοράς και χρήσης της. Λύση: strong-σύλληψη της αδύναμης αναφοράς σε μια τοπική μεταβλητή.

swift
// Συνθήκη ανταγωνισμού με weak σε πολυνηματικό περιβάλλον
func performAsync() {
    weak var weakSelf = self
    queue.async {
        // ⚠️ weakSelf μπορεί να είναι nil μεταξύ ελέγχου και χρήσης
        if weakSelf != nil {
            weakSelf!.doSomething()  // CRASH αν έγινε nil
        }
    }
}

// ✅ Διόρθωση: ισχυρή σύλληψη για τη διάρκεια χρήσης
func performAsyncSafe() {
    queue.async { [weak self] in
        guard let strongSelf = self else { return }
        strongSelf.doSomething()  // strongSelf — τοπική strong αναφορά
    }
}

Στην ασφαλή παραλλαγή, το weak self συλλαμβάνεται, στη συνέχεια αμέσως αποσυσκευάζεται σε μια τοπική strong μεταβλητή strongSelf. Εάν το self είναι ακόμα ζωντανό — θα παραμείνει ζωντανό κατά την εκτέλεση του μπλοκ. Εάν όχι — το guard ενεργοποιείται και ο κώδικας δεν εκτελείται. Αυτό το idiom — το τυπικό μοτίβο για ασύγχρονα closures στη Swift.

UIView και weak outlet

IBOutlet στο Interface Builder πρέπει να είναι weak, επειδή η ιεραρχία προβολής ήδη κρατά strong αναφορά στο subview. Η αντιγραφή της strong αναφοράς στον ελεγκτή δεν δημιουργεί retain cycle, αλλά είναι περιττή. Η αδύναμη αναφορά σε outlet — σύσταση της Apple, αν και πολλοί προγραμματιστές χρησιμοποιούν strong για απλοποίηση του κώδικα.

Συχνές Ερωτήσεις

Μπορεί μια weak αναφορά να δείχνει σε ένα αντικείμενο που δεν έχει ακόμα δημιουργηθεί;

Όχι, το weak μπορεί να δείχνει μόνο σε υπάρχον αντικείμενο ή nil. Κατά τη δημιουργία ενός νέου αντικειμένου, πρώτα λαμβάνετε μια strong αναφορά (μέσω του αρχικοποιητή), και μόνο τότε μπορείτε να εκχωρήσετε μια αδύναμη αναφορά. weak nil στην αρχή — φυσιολογική κατάσταση.

Γιατί το weak λειτουργεί μόνο με class-types;

Το Weak βασίζεται στο ARC, το οποίο διαχειρίζεται μόνο τύπους αναφοράς (κλάσεις). Οι τύποι τιμής (struct, enum) αντιγράφονται κατά την εκχώρηση και δεν έχουν retain count. Για αδύναμη σύνδεση τύπων τιμής χρησιμοποιήστε closures ή wrapper σε class με weak ιδιότητα.

Πώς επηρεάζει το weak την απόδοση σε έναν βρόχο;

Κάθε πρόσβαση σε weak αναφορά εκτελεί ένα lookup στον πίνακα runtime. Σε έναν βρόχο με εκατομμύρια επαναλήψεις αυτό μπορεί να είναι 2–5 φορές πιο αργό από μια strong αναφορά. Για καυτές διαδρομές αντιγράψτε το weak σε μια τοπική strong μεταβλητή πριν από τον βρόχο.

Πότε μπορεί μια weak αναφορά να γίνει nil απροσδόκητα;

Όταν όλες οι strong αναφορές στο αντικείμενο χαθούν — στο τέλος του πεδίου εμβέλειας, κατά την επαναφορά ιδιότητας, κατά το κλείσιμο οθόνης. Σε πολυνηματικό περιβάλλον αυτό μπορεί να συμβεί μεταξύ δύο γραμμών κώδικα. Πάντα ελέγχετε το weak μέσω guard let ή if let.

Σε τι διαφέρει το weak από το __weak στο Objective-C;

Σημασιολογικά ταυτόσημο: και τα δύο παρέχουν zeroing weak. Διαφορές: Η Swift απαιτεί optional τύπο και var, το Objective-C χρησιμοποιεί τροποποιητή property. Το Objective-C υποστηρίζει επίσης __unsafe_unretained — αδύναμη αναφορά χωρίς zeroing (κίνδυνος αιωρούμενου δείκτη).

Περίληψη

  • Weak Reference — μη κατέχουσα αναφορά που δεν αυξάνει το retain count και μηδενίζεται αυτόματα κατά την αποδέσμευση
  • Σύνταξηweak var + προαιρετικός τύπος; μόνο class-types και πρωτόκολλα AnyObject
  • Zeroing weak — το runtime μηδενίζει όλες τις weak αναφορές στο ελευθερωμένο αντικείμενο, αποτρέποντας αιωρούμενους δείκτες
  • Σενάρια — εκπρόσωποι, parent-child με ανάδραση, ασύγχρονα closures ([weak self])
  • Weak vs Unowned — weak μηδενίζεται (ασφαλές), unowned δεν μηδενίζεται (κίνδυνος crash, αλλά non-optional)
  • Απόδοση — weak πιο αργό από strong λόγω lookup στον πίνακα runtime; για καυτές διαδρομές αντιγράψτε σε strong
  • Σύσταση — εάν δεν είστε σίγουροι για τις εγγυήσεις ζωής — επιλέξτε weak

Θα αναπτύξουμε μια εφαρμογή για κινητά έτοιμη για χρήση

Η IT Sectr δημιουργεί εφαρμογές iOS και Android για νεοφυείς επιχειρήσεις και επιχειρήσεις από το 2017. Θα σας συμβουλεύσουμε και θα προτείνουμε την καλύτερη λύση.

Συζήτηση έργου

Διαβάστε επίσης