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)
    }
}