गैर-बचने वाले पैरामीटर के बंद उपयोग से इसे बचने की अनुमति मिल सकती है


139

मेरे पास एक प्रोटोकॉल है:

enum DataFetchResult {
    case success(data: Data)
    case failure
}

protocol DataServiceType {
    func fetchData(location: String, completion: (DataFetchResult) -> (Void))
    func cachedData(location: String) -> Data?
}

एक उदाहरण कार्यान्वयन के साथ:

    /// An implementation of DataServiceType protocol returning predefined results using arbitrary queue for asynchronyous mechanisms.
    /// Dedicated to be used in various tests (Unit Tests).
    class DataMockService: DataServiceType {

        var result      : DataFetchResult
        var async       : Bool = true
        var queue       : DispatchQueue = DispatchQueue.global(qos: .background)
        var cachedData  : Data? = nil

        init(result : DataFetchResult) {
            self.result = result
        }

        func cachedData(location: String) -> Data? {
            switch self.result {
            case .success(let data):
                return data
            default:
                return nil
            }
        }

        func fetchData(location: String, completion: (DataFetchResult) -> (Void)) {

            // Returning result on arbitrary queue should be tested,
            // so we can check if client can work with any (even worse) implementation:

            if async == true {
                queue.async { [weak self ] in
                    guard let weakSelf = self else { return }

                    // This line produces compiler error: 
                    // "Closure use of non-escaping parameter 'completion' may allow it to escape"
                    completion(weakSelf.result)
                }
            } else {
               completion(self.result)
            }
        }
    }

ऊपर दिए गए कोड को Swift3 (Xcode8-beta5) में संकलित और काम किया गया है, लेकिन अब बीटा 6 के साथ काम नहीं करता है। क्या आप मुझे अंतर्निहित कारण की ओर इशारा कर सकते हैं?


5
यह एक बहुत ही बढ़िया लेख है कि स्विफ्ट 3
हनी

1
इससे कोई मतलब नहीं है कि हमें यह करना है। किसी अन्य भाषा को इसकी आवश्यकता नहीं है।
एंड्रयू कोस्टर

जवाबों:


243

यह फ़ंक्शन प्रकार के मापदंडों के लिए डिफ़ॉल्ट व्यवहार में बदलाव के कारण है। स्विफ्ट 3 से पहले (विशेष रूप से एक्सकोड 8 बीटा 6 वाले जहाजों का निर्माण), वे बचने के लिए डिफ़ॉल्ट होंगे - उन्हें @noescapeसंग्रहीत या कब्जा करने से रोकने के लिए आपको उन्हें चिह्नित करना होगा , जो गारंटी देता है कि वे अवधि को रेखांकित नहीं करेंगे। फ़ंक्शन कॉल की।

हालाँकि, अब @noescapeफ़ंक्शन-टाइप किए गए मापदंडों के लिए डिफ़ॉल्ट है। यदि आप ऐसे कार्यों को संग्रहीत या कैप्चर करना चाहते हैं, तो आपको अब उन्हें चिह्नित करने की आवश्यकता है @escaping:

protocol DataServiceType {
  func fetchData(location: String, completion: @escaping (DataFetchResult) -> Void)
  func cachedData(location: String) -> Data?
}

func fetchData(location: String, completion: @escaping (DataFetchResult) -> Void) {
  // ...
}

इस बदलाव के बारे में अधिक जानकारी के लिए स्विफ्ट इवोल्यूशन प्रस्ताव देखें ।


2
लेकिन, आप एक बंद का उपयोग कैसे करते हैं ताकि यह भागने की अनुमति न दे?
एनेको अलोंसो

6
@EnekoAlonso पूरी तरह से निश्चित नहीं है कि आप क्या पूछ रहे हैं - आप या तो फ़ंक्शन में सीधे एक गैर-भागने वाले फ़ंक्शन पैरामीटर को कॉल कर सकते हैं, या गैर-भागने वाले क्लोजर में कैप्चर किए जाने पर आप इसे कॉल कर सकते हैं। इस मामले में, जैसा कि हम अतुल्यकालिक कोड के साथ काम कर रहे हैं, इस बात की कोई गारंटी नहीं है कि asyncफ़ंक्शन पैरामीटर (और इसलिए completionफ़ंक्शन) को fetchDataबाहर निकलने से पहले बुलाया जाएगा - और इसलिए होना चाहिए @escaping
हामिश

कुरूपता महसूस करता है कि हमें प्रोटोकॉल के लिए विधि हस्ताक्षर के रूप में @ शेष को निर्दिष्ट करना है ... क्या हमें क्या करना चाहिए? प्रस्ताव नहीं कहता है! : एस
सज्जन

1
@ सज्जोन वर्तमान में, आपको उस आवश्यकता के कार्यान्वयन में @escapingएक @escapingपैरामीटर के साथ एक प्रोटोकॉल आवश्यकता में एक पैरामीटर से मेल खाना चाहिए (और गैर-बचने के मापदंडों के लिए इसके विपरीत)। स्विफ्ट 2 में भी ऐसा ही था @noescape
हमिश


30

चूंकि @noescape डिफ़ॉल्ट है, इसलिए त्रुटि को ठीक करने के लिए 2 विकल्प हैं:

1) जैसा कि @ हमीश ने अपने जवाब में बताया है, यदि आप परिणाम के बारे में परवाह करते हैं और वास्तव में इसे बचाना चाहते हैं तो @ केसिंग के रूप में पूरा होने को चिह्नित करें (जैसा कि संभवतः यूनिट टेस्ट के साथ @ लुकाज़ के प्रश्न में उदाहरण के रूप में और संभावना के रूप में) समापन)

func fetchData(location: String, completion: @escaping (DataFetchResult) -> Void)

या

2) जब आप परिणाम के बारे में परवाह नहीं करते हैं, तो परिणामों को पूरी तरह से छोड़ने के लिए वैकल्पिक वैकल्पिक बनाकर डिफ़ॉल्ट @noescape व्यवहार रखें। उदाहरण के लिए जब उपयोगकर्ता पहले ही "चला गया" है और कॉलिंग व्यू कंट्रोलर को केवल इसलिए मेमोरी में नहीं लटकाना है क्योंकि कुछ लापरवाह नेटवर्क कॉल थी। जैसा कि यह मेरे मामले में था जब मैं यहाँ आया था उत्तर की तलाश में था और नमूना कोड मेरे लिए बहुत प्रासंगिक नहीं था, इसलिए @noescape को चिह्नित करना सबसे अच्छा विकल्प नहीं था, घटना हालांकि यह पहली नज़र से केवल एक ही लग रहा था।

func fetchData(location: String, completion: ((DataFetchResult) -> Void)?) {
   ...
   completion?(self.result)
}
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.