Sobes.tech
Middle+

Πάντα χρειάζεται να χρησιμοποιείτε weak ή unowned στη λίστα σύλληψης;

sobes.tech AI

Απάντηση από AI

Όχι, όχι πάντα.

Το weak και το unowned χρησιμοποιούνται για την αποτροπή κύκλων ισχυρών αναφορών (retain cycles) στα closures, όπου το closure διατηρεί ισχυρή αναφορά σε ένα αντικείμενο, και το αντικείμενο διατηρεί ισχυρή αναφορά στο closure.

Το weak ή unowned πρέπει να χρησιμοποιούνται όταν το closure ρίχνει μια ισχυρή αναφορά σε self ή σε άλλο αναφορικό τύπο, και υπάρχει πιθανότητα δημιουργίας κύκλου ισχυρών αναφορών.

Περιπτώσεις όπου δεν χρειάζεται να χρησιμοποιήσετε weak/unowned:

  • Το closure ρίχνει τιμές τύπων (δομές, enumerations, tuples). Αντιγράφονται κατά το ρίξιμο, και δεν δημιουργούνται κύκλοι ισχυρών αναφορών.
  • Το closure ρίχνει αναφορικό τύπο, αλλά δεν υπάρχει διπλός ισχυρός κύκλος. Για παράδειγμα, αν το closure δημιουργείται τοπικά μέσα σε μια μέθοδο και δεν αποθηκεύεται από ένα αντικείμενο που αναφέρεται σε αυτό.
  • Το closure έχει βραχύ χρόνο ζωής και θα εκτελεστεί πριν το αντικείμενο που αναφέρεται σε αυτό απελευθερωθεί. Για παράδειγμα, ένα closure που περνάει ως Completion Handler σε μια μέθοδο και εκτελείται άμεσα.

Επιλογή μεταξύ weak και unowned:

  • weak: Χρησιμοποιείται όταν το ριγμένο αντικείμενο μπορεί να γίνει nil οποιαδήποτε στιγμή πριν την εκτέλεση του closure. Η αναφορά δηλώνεται ως προαιρετική (ObjectType?). Απαιτείται έλεγχος για nil πριν τη χρήση.
  • unowned: Χρησιμοποιείται όταν είναι γνωστό ότι το ριγμένο αντικείμενο πάντα θα υπάρχει κατά την εκτέλεση του closure. Η αναφορά είναι μη-προαιρετική (ObjectType). Δεν απαιτείται έλεγχος για nil. Η χρήση unowned σε αντικείμενο που έχει γίνει nil θα οδηγήσει σε σφάλμα (runtime error).
// Παράδειγμα, όταν δεν χρειάζεται `weak`/`unowned` (κατάληψη δομής)
struct MyStruct {
    var value: Int
}

let myStruct = MyStruct(value: 10)
let closure = {
    print(myStruct.value) // Ρίχνει αντίγραφο του myStruct
}
closure()

// Παράδειγμα, όταν δεν χρειάζεται `weak`/`unowned` (τοπικό κλείσιμο)
class MyClass {
    func doSomething() {
        let localValue = 5
        let localClosure = {
            print(self.description) // Ρίχνει το self, αλλά το closure δεν αποθηκεύεται από το MyClass
        }
        localClosure()
    }

    var description: String {
        return "Αντίγραφο του MyClass"
    }
}

let myObject = MyClass()
myObject.doSomething()

// Παράδειγμα, όταν χρειάζεται `weak` (πιθανά `nil`)
class AsyncOperation {
    var completionHandler: (() -> Void)?

    func performOperation() {
        DispatchQueue.global().async {
            print("Ολοκλήρωση λειτουργίας")
            self.completionHandler?()
        }
    }

    deinit {
        print("AsyncOperation απελευθερώθηκε")
    }
}

class ViewController {
    var op: AsyncOperation?

    func setupOperation() {
        op = AsyncOperation()
        op?.completionHandler = { [weak self] in
            guard let self = self else { return }
            print("Ολοκλήρωση handler στο ViewController: \(self)")
        }
    }

    deinit {
        print("ViewController απελευθερώθηκε")
    }
}

// Παράδειγμα, όταν χρειάζεται `unowned` (εγγυημένα υπάρχει)
class Button {
    var action: (() -> Void)?

    func tap() {
        action?()
    }

    deinit {
        print("Button απελευθερώθηκε")
    }
}

class ViewControllerWithButton {
    var button: Button

    init() {
        button = Button()
        button.action = { [unowned self] in
            print("Το κουμπί πατήθηκε στο ViewControllerWithButton: \(self)")
        }
    }

    deinit {
        print("ViewControllerWithButton απελευθερώθηκε")
    }
}