स्विफ्ट @ एसफॉर्मिंग और कंप्लीशन हैंडलर


99

मैं स्विफ्ट के 'क्लोजर' को और अधिक सटीक रूप से समझने की कोशिश कर रहा हूं।

लेकिन @escapingऔर Completion Handlerसमझना बहुत मुश्किल है

मैंने कई स्विफ्ट पोस्टिंग और आधिकारिक दस्तावेजों को खोजा, लेकिन मुझे लगा कि यह अभी भी पर्याप्त नहीं है।

यह आधिकारिक दस्तावेजों का कोड उदाहरण है

var completionHandlers: [()->Void] = []

func someFunctionWithEscapingClosure(completionHandler: @escaping ()->Void){
    completionHandlers.append(completionHandler)
}

func someFunctionWithNoneescapingClosure(closure: ()->Void){
    closure()
}

class SomeClass{
    var x:Int = 10
    func doSomething(){
        someFunctionWithEscapingClosure {
            self.x = 100
            //not excute yet
        }
        someFunctionWithNoneescapingClosure {
            x = 200
        }
    }
}

let instance = SomeClass()
instance.doSomething()
print(instance.x)

completionHandlers.first?() 
print(instance.x)

मैंने सुना है कि उपयोग करने के दो तरीके और कारण हैं @escaping

पहला एक क्लोजर के भंडारण के लिए है, दूसरा Async ऑपरेटिंग उद्देश्यों के लिए है।

निम्नलिखित मेरे प्रश्न हैं :

सबसे पहले, यदि doSomethingनिष्पादित होता है , तो someFunctionWithEscapingClosureक्लोजर पैरामीटर के साथ निष्पादित होगा और उस क्लोजर को वैश्विक चर सरणी में सहेजा जाएगा।

मुझे लगता है कि क्लोजर है {self.x = 100}

कैसे self{self.x = 100} जो ग्लोबल वैरिएबल में सेव है, उस ऑब्जेक्ट से completionHandlersकनेक्ट हो सकता instanceहै SomeClass?

दूसरा, मैं someFunctionWithEscapingClosureइस तरह से समझ रहा हूं ।

completionHandlerवैश्विक वैरिएबल 'पूराहैंडलर' के लिए स्थानीय वैरिएबल क्लोजर को स्टोर करनाwe using भागने वाले कीवर्ड!

@escapingकीवर्ड someFunctionWithEscapingClosureरिटर्न के बिना , स्थानीय चर completionHandlerमेमोरी से हटा देगा

@escaping कि स्मृति में बंद रखना है

क्या यह सही है?

अंत में, मैं सिर्फ इस व्याकरण के अस्तित्व के बारे में आश्चर्य करता हूं।

शायद यह एक बहुत ही अल्पविकसित प्रश्न है।

यदि हम कुछ विशिष्ट कार्य के बाद कुछ कार्य निष्पादित करना चाहते हैं। एक विशिष्ट फ़ंक्शन कॉल के बाद हम केवल कुछ फ़ंक्शन को क्यों नहीं कहते हैं?

उपरोक्त पैटर्न का उपयोग करने और एक पलायन कॉलबैक फ़ंक्शन का उपयोग करने के बीच क्या अंतर हैं?

जवाबों:


123

स्विफ्ट समापन हैंडलर भागने और गैर-भागने:

जैसा कि बॉब ली ने अपने ब्लॉग पोस्ट स्विफ्ट में बॉब के साथ पूरा करने के बारे में बताया :

मान लें कि उपयोगकर्ता उपयोग करते समय कोई ऐप अपडेट कर रहा है। आप निश्चित रूप से उपयोगकर्ता को सूचित करना चाहते हैं जब यह किया जाता है। आप संभवतः एक बॉक्स को पॉप अप करना चाहते हैं जो कहता है, "बधाई हो, अब, आप पूरी तरह से आनंद ले सकते हैं!"

तो, डाउनलोड पूरा होने के बाद ही आप कोड का एक ब्लॉक कैसे चलाते हैं? इसके अलावा, आप कैसे कुछ वस्तुओं को चेतन नियंत्रक के बाद अगले स्थान पर ले जाते हैं? खैर, हम यह पता लगाने जा रहे हैं कि एक मालिक की तरह कैसे डिजाइन किया जाए।

मेरी विस्तृत शब्दावली सूची के आधार पर, पूर्ण हैंडलर खड़े होते हैं

जब चीजें हो चुकी हों तब सामान करें

बॉब की पोस्ट पूर्ण हैंडलर के बारे में स्पष्टता प्रदान करती है (एक डेवलपर दृष्टिकोण से यह वास्तव में परिभाषित करता है कि हमें क्या समझने की आवश्यकता है)।

@ बंद करने वाले बंद:

जब कोई फ़ंक्शन तर्कों में एक क्लोजर पास करता है, तो फ़ंक्शन के शरीर के निष्पादन के बाद इसका उपयोग करता है और संकलक को वापस करता है। जब फ़ंक्शन समाप्त होता है, तो पारित बंद होने की गुंजाइश मौजूद होती है और स्मृति में अस्तित्व होता है, जब तक कि क्लोजर निष्पादित नहीं हो जाता।

समारोह में बंद होने से बचने के कई तरीके हैं:

  • भंडारण: जब आपको वैश्विक वैरिएबल में क्लोजर को स्टोर करने की आवश्यकता होती है, तो प्रॉपर्टी या किसी अन्य स्टोरेज जो कॉलिंग फ़ंक्शन के मेमोरी अतीत में मौजूद हैं, निष्पादित हो जाते हैं और कंपाइलर को वापस कर देते हैं।

  • एसिंक्रोनस निष्पादन: जब आप डिस्पैच कतार पर अतुल्यकालिक रूप से क्लोजर को निष्पादित कर रहे हैं, तो कतार आपके लिए मेमोरी में क्लोजर को धारण करेगी, भविष्य में उपयोग किया जा सकता है। इस मामले में आपको पता नहीं है कि कब बंद हो जाएगा।

जब आप इन परिदृश्यों में क्लोजर का उपयोग करने का प्रयास करते हैं, तो स्विफ्ट कंपाइलर त्रुटि दिखाएगा:

त्रुटि स्क्रीनशॉट

इस विषय के बारे में अधिक स्पष्टता के लिए आप इस पोस्ट को माध्यम पर देख सकते हैं ।

एक और बिंदुओं को जोड़ना, जिसे प्रत्येक ios डेवलपर को समझने की आवश्यकता है:

  1. बचने के समापन : एक भागने का समापन एक बंद होता है जिसे उस फ़ंक्शन के बाद कहा जाता है जिसे रिटर्न में पारित किया गया था। दूसरे शब्दों में, यह उस कार्य को रेखांकित करता है जिसे इसे पारित किया गया था।
  2. गैर-भागने वाला बंद : एक बंद जिसे उस फ़ंक्शन के भीतर बुलाया जाता है जिसे वह पारित किया गया था, यानी लौटने से पहले।

@ शभाकर, क्या होगा अगर हम एक स्टोर को बंद कर देते हैं लेकिन बाद में कॉल नहीं करते हैं। या अगर विधि को दो बार कहा जाता है, लेकिन हमने केवल एक बार बंद कर दिया। चूंकि हम जानते हैं कि परिणाम समान है।
user1101733

@ user1101733 मुझे लगता है कि आप बंद होने से बचने के बारे में बात कर रहे हैं, जब तक आप कॉल नहीं करेंगे, क्लोजर निष्पादित नहीं होगा। उपरोक्त उदाहरण में यदि कॉल डोसिमेंटिंग विधि 2 बार 2 पूर्णहैंडलर ऑब्जेक्ट को पूरा करने वाले हेंडलर सरणी में जोड़ देगा। यदि आप पहले ऑब्जेक्ट्स को पूरा करने वाले हेंडलर्स एरे से लेते हैं और कॉल करते हैं तो यह निष्पादित होगा लेकिन पूरा करने वाले हेंडलर सरणी काउंट वही (2) रहेंगे।
दीपक

@Deepak, भागने के बंद होने के बारे में हाँ। मान लीजिए कि हम सरणी का उपयोग नहीं करते हैं और सामान्य चर का उपयोग बंद संदर्भ को संग्रहीत करने के लिए करते हैं क्योंकि हम सबसे हालिया कॉल को निष्पादित करते हैं। क्या कुछ स्मृति पिछले बंदों द्वारा कब्जा कर लिया जाएगा जो कभी नहीं बुलाएंगे?
user1101733 21

1
@ user1101733 क्लोजर रेफरेंस टाइप (क्लास की तरह) होते हैं, जब आप वेरिएबल को नए क्लोजर असाइन करते हैं तो प्रॉपर्टी / वैरिएबल नए क्लोजर की ओर इशारा करेगा, इसलिए ARC पिछले क्लोजर के लिए मेमोरी डिलिट करेगा।
दीपक

28

यहाँ उदाहरणों का एक छोटा वर्ग है, जिसका उपयोग मैं खुद को याद दिलाने के लिए करता हूँ कि कैसे @ काम करता है।

class EscapingExamples: NSObject {

    var closure: (() -> Void)?

    func storageExample(with completion: (() -> Void)) {
        //This will produce a compile-time error because `closure` is outside the scope of this
        //function - it's a class-instance level variable - and so it could be called by any other method at
        //any time, even after this function has completed. We need to tell `completion` that it may remain in memory, i.e. `escape` the scope of this
        //function.
        closure = completion
        //Run some function that may call `closure` at some point, but not necessary for the error to show up.
        //runOperation()
    }

    func asyncExample(with completion: (() -> Void)) {
        //This will produce a compile-time error because the completion closure may be called at any time
        //due to the async nature of the call which precedes/encloses it.  We need to tell `completion` that it should
        //stay in memory, i.e.`escape` the scope of this function.
        DispatchQueue.global().async {
            completion()
        }
    }

    func asyncExample2(with completion: (() -> Void)) {
        //The same as the above method - the compiler sees the `@escaping` nature of the
        //closure required by `runAsyncTask()` and tells us we need to allow our own completion
        //closure to be @escaping too. `runAsyncTask`'s completion block will be retained in memory until
        //it is executed, so our completion closure must explicitly do the same.
        runAsyncTask {
            completion()
        }
    }





    func runAsyncTask(completion: @escaping (() -> Void)) {
        DispatchQueue.global().async {
            completion()
        }
    }

}

2
यह कोड सही नहीं है। यह @escapingक्वालीफायर को मिस कर रहा है।
रोब

मुझे यह सबसे ज्यादा पसंद हैi.e. escape the scope of this function.
गैल
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.