Sobes.tech
Middle

ყოველთვის საჭიროა weak ან unowned გამოყენება დაკავების სიაში?

sobes.tech AI

პასუხი AI-სგან

ხოლო, არა ყოველთვის.

გამოყენებულია weak ან unowned Capture List-ში, რათა თავიდან აიცილოს retain cycle-ები.

Retain cycle-ები წარმოიქმნება, როდესაც ორი ან მეტი ობიექტი ძლიერად მიმართავენ ერთმანეთს, რაც არ აძლევს მეხსიერების გათავისუფლებას.

გამოყენება weak ან unowned საჭიროა, როდესაც დახურვა ძლიერად მიმართავს self (ან სხვა კლასი/აკტორის მაგალითის ცვლადს), და თავის მხრივ self (ან სხვა ობიექტი) აქვს ძლიერი მიკერძოება ამ დახურვაზე.

მაგალითები სიტუაციების, სადაც ეს აუცილებელია:

  • ასინქრონული ოპერაციები (სერვისის მოთხოვნები, მონაცემების დამუშავება)
  • Observer პატერნი
  • ანიმაციური ბლოკები
  • Dispatch რიგები

თუ დახურვა არ იჭერს self (ან სხვა კლასი/აკტორის ცვლადს, რომელიც აქვს ძლიერი მიკერძოება დახურვაზე), ან იჭერს სტრუქტურული ტიპებს/სია, weak ან unowned გამოყენება საჭირო არ არის.

weak და unowned შორის განსხვავება:

ტიპი აღწერა როდესაც გამოიყენება
weak სუსტი (ოპციონალური) მიკერძოება. შეიძლება გახდეს nil. როდესაც დახურვის სიცოცხლის ციკლი მოკლეა ან თანაბარია იჭერით ობიექტის სიცოცხლის ციკლთან, და ობიექტი შეიძლება გახდეს nil დახურვის დასრულებამდე.
unowned უქმნელი (ოპციონალური არა) მიკერძოება. არ შეიძლება გახდეს nil. როდესაც დახურვის სიცოცხლის ციკლი მოკლეა ან თანაბარია იჭერით ობიექტის სიცოცხლის ციკლთან, და გარანტირებული არის, რომ ობიექტი იქნება არსებობაში დახურვის დასრულებამდე. თუ ობიექტი გათავისუფლდა ადრე, მოხდება შეცდომა (unowned მიკერძოება deallocated instance-ზე).

weak გამოყენების მაგალითი:

class MyClass {
    var name: String = "Test"

    func performAsyncOperation() {
        DispatchQueue.global().async { [weak self] in
            guard let self = self else {
                return
            }
            print("ოპერაცია დასრულდა עבור \(self.name)")
        }
    }

    deinit {
        print("MyClass განულდა")
    }
}

unowned გამოყენების მაგალითი:

class ViewController {
    var dismissButton: UIButton!

    func setupButton() {
        dismissButton.addTarget(self, action: #selector(dismiss), for: .touchUpInside)
        // ამ შემთხვევაში, დახურვა იჭერს `self`-ს.
        // თუ ეს იქნებოდა დახურვა, მაგალითად, UIControl.Event-ის handler-ში,
        // და ViewController-ის არსებობა გარანტირებულია, სანამ ღილაკზე დაწკაპუნება მოხდება:
        // dismissButton.addAction(UIAction { [unowned self] _ in
        //     self.dismiss(animated: true, completion: nil)
        // }, for: .touchUpInside)
    }

    @objc func dismiss() {
        // დისპლეის კონტროლერი გაუქმება
    }
}

თუ დახურვა არ ქმნის retain cycle-ს, weak ან unowned გამოყენება საჭირო არ არის და შეიძლება გამოიწვიოს ზედმეტი შემოწმებები (weak-ის შემთხვევაში) ან შესაძლო შეცდომები (unowned-ის შემთხვევაში).

მაგალითად, დახურვა, რომელიც არ იჭერს self-ს ან სხვა ობიექტს, რომლის ძლიერი მიკერძოება აქვს:

func fetchData(completion: @escaping (Data) -> Void) {
    URLSession.shared.dataTask(with: URL(string: "...")!) { data, response, error in
        guard let data = data else { return }
        // აქ არ იჭერება `self` ან სხვა ობიექტები, რომლებიც შეიძლება გამოიწვიოს retain cycle.
        completion(data)
    }.resume()
}

ამ შემთხვევაში [weak self] ან [unowned self] გამოყენება საჭირო არ არის.