Sobes.tech
Middle

ყოველთვის უნდა მიუთითოთ `self` პარამეტრი დახურვებში?

sobes.tech AI

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

არა, ყოველთვის არა:

საჭიროა self-ის ცხადყოფა დახურულში მხოლოდ შემდეგ შემთხვევებში:

  1. არ-გაშლილ (non-escaping) დახურულში: ეს კომპილატორის მოთხოვნაა self-ის აშკარად აღნიშვნისთვის. მიუხედავად იმისა, რომ захват ტექნიკურად არსებობს, ის ხდება ფუნქციის შიგნით და არ ქმნის ძლიერი მითითებების ციკლს.
  2. გაშლილ (escaping) დახურულში, როდესაც self-ის захватыვა იმპლიციტურად (implicit capture): თუ დახურული ფუნქცია გადის იმ ფუნქციის ფარგლებს, სადაც ის გამოაცხადა (მაგალითად, გადაეცემა ასინქრონულ ოპერაციას, ინახება თვისებაში და ა.შ.), და თქვენ self-ს უხილავად აყვანთ, კომპილატორი მოითხოვს self-ის ცხადყოფას, რათა თავიდან აიცილოს ძლიერი მითითებების ციკლი.

ყველა სხვა შემთხვევებში (მაგალითად, weak self ან unowned self-ის სია გამოყენებით) self-ის მითითება საჭირო არაა, რადგან საკუთრების ურთიერთობა მკაფიო კონტროლირებულია.

მაგალითი:

class MyClass {
    var value = 10

    func doSomething() {
        // არ-გაშლილ დახურულში - საჭიროა `self`
        UIView.animate(withDuration: 0.3) {
            self.value = 20
        }
    }

    func fetchData(completion: @escaping (Result<String, Error>) -> Void) {
        // გაშლილ დახურულში, სადაც `self`-ის აყვანა უხილავია - საჭიროა `self`
        // ეს შეიძლება გამოიწვიოს ძლიერი მითითებების ციკლი, თუ `[weak self]` არ გამოიყენება
        someAsyncOperation { data, error in
            if let data = data {
                // საჭიროა `self` უხილავი აყვანით
                self.processData(data)
                completion(.success("Data processed"))
            } else if let error = error {
                completion(.failure(error))
            }
        }
    }

    func უსაფრთხოFetchData(completion: @escaping (Result<String, Error>) -> Void) {
         // გაშლილ დახურულში, `[weak self]`-ით - `self?` ოპციონალურია და არ საჭიროებს `self.`-ის გამოყოფას
        someAsyncOperation { [weak self] data, error in
            guard let self = self else { return } // `self`-ის გახსნა ოპციონალურია

            if let data = data {
                self.processData(data) // `self.`-ის გამოყენება საჭირო არაა
                completion(.success("Data processed"))
            } else if let error = error {
                completion(.failure(error))
            }
        }
    }

    private func processData(_ data: String) {
        print("Processing \(data)")
    }
}

func someAsyncOperation(completion: @escaping (String?, Error?) -> Void) {
    // ასინქრონული ოპერაციის იმიტაცია
    DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
        completion("Some data", nil)
    }
}