2014-10-26 12 views
5

मुझे पता चला कि स्विफ्ट क्लोजर मेरी अपेक्षा के विपरीत कैप्चर किए गए चर को बरकरार नहीं रख रहे हैं।क्या स्विफ्ट क्लोजर कैप्चर वैरिएबल बनाए रखता है?

class AAA { 
} 
var a1 = AAA() as AAA?     // expects RC == 1 
var a2 = {()->AAA? in return a1 }  // expects RC == 2, retained by `Optional<AAA>` 
a1  = nil       // expects RC == 1 
a2()          // prints nil, ???? 

मैं इस से बहुत उलझन में हूं क्योंकि मुझे विश्वास है कि कब्जे वाले चर डिफ़ॉल्ट रूप से बनाए रखा जाएगा। लेकिन, अगर मैं इसे कैप्चरिंग सूची का उपयोग करके स्पष्ट रूप से कैप्चर करता हूं, तो इसे बरकरार रखा जा रहा है।

class AAA { 
} 
var a1 = AAA() as AAA? 
var a2 = { [a1]()->AAA? in return a1 } 
a1  = nil 
a2() // prints {AAA}, alive as expected. 

मैंने स्विफ्ट मैनुअल को फिर से पढ़ा, लेकिन मुझे संबंधित विवरण नहीं मिला। कैप्चरिंग सूची का उपयोग unowned को स्पष्ट रूप से सेट करने के लिए किया जाता है, और मैं अभी भी उलझन में हूं। सही व्यवहार क्या है और यह क्यों होता है?

उत्तर

5

हाँ कि Capturing Values में दर्ज है:

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

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

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

+1

सच नहीं है। उदाहरण के लिए, 'func test() {var x = 42; चलो f = {println (x)}; एक्स = 43; एफ()}; परीक्षण() 'प्रिंट '43', जिसका अर्थ है कि क्लोजर संदर्भ द्वारा परिवर्तनीय को कैप्चर करता है, भले ही यह इसे संशोधित किए बिना चर का संदर्भ देता है। – newacct

+0

धन्यवाद @ न्यूवेक्ट। यद्यपि मैंने जो लिखा है, वह स्पष्ट रूप से नहीं कह रहा है, प्रलेखन ने मुझे लगता है कि यह इस तरह से काम करता है। – Antonio

+0

क्या संदर्भ के द्वारा संकलक को पकड़ने के लिए मजबूर करने का कोई तरीका है? मेरे पास एक ऐसा मामला है जहां मेरे पास साझा मूल्य तक पहुंचने का प्रयास करने वाले दो बंद हैं। एक बंद कैप्चर किए गए मान को संशोधित करता है और दूसरा इसे केवल पढ़ता है। हालांकि, जो इसे पढ़ता है वह एक प्रति प्राप्त कर रहा है। मुझे इसे संदर्भ द्वारा कैप्चर करने के लिए मजबूर करने की आवश्यकता है। –

1

मैंने ऐप्पल डेवलपर फोरम पर एक ही प्रश्न पोस्ट किया, और a discussion था। हालांकि लोग संदर्भ के बारे में बात नहीं कर रहे हैं, लेकिन मुझे कुछ विचार मिलते हैं। यहां मेरा निष्कर्ष है:

  • एक मान हमेशा कॉपी किया जाता है जब यह एक नए (स्पष्ट) नाम से बंधे होते हैं। var या let पर ध्यान दिए बिना।
  • संदर्भ प्रकारों में की प्रतिलिपि का अर्थ आरसी + 1 है। क्योंकि यह सी ++ shared_ptr<T> जैसे मजबूत सूचक की प्रतिलिपि बना रहा है।
  • समापन (बंद कैप्चरिंग) कुछ भी नहीं बदलता क्योंकि कोई नया नाम नहीं है। यह वही है जैसा आप उन्हें एक ही ढेर में उपयोग करते हैं। यह द्वारा संदर्भ का अर्थ है। आरसी से संबंधित कुछ भी नहीं, और आरसी नहीं बदलता क्योंकि यह सी ++ में संदर्भों की तरह कुछ है।
  • कैप्चरिंग सूची एक स्पष्ट (let) नाम है। तो यह प्रतिलिपि बनाने का कारण बनता है और यह आरसी + 1 बनाता है।
  • जब किसी फ़ंक्शन से बंद किया जाता है, तो यह किसी नाम से बाध्य हो सकता है। अगर ऐसा होता है, तो आरसी + 1 नाम के लिए नए बाध्यकारी होने के कारण।
  • आरसी -1 जब एक मूल्य (इसलिए एक मजबूत सूचक) अपने नाम से अनबाउंड करता है।
  • self का लागू संदर्भ एकमात्र अपवाद है जो अंतर्निहित नाम बाध्यकारी बनाता है।

और इन नियमों को तोड़ने के बिना कई अनुकूलन हैं, वैसे भी वे केवल कार्यान्वयन विवरण हैं।

1

आपका उदाहरण समझ में नहीं आता @newacct। परिवर्तनीय एक्स वास्तव में ब्लॉक के बाहर संशोधित है। स्विफ्ट यह पता लगाने के लिए पर्याप्त स्मार्ट है कि आप बंद करने के अंदर या बाहर एक चर को संशोधित करते हैं या नहीं।

दस्तावेज़ कहते हैं:

एक अनुकूलन के रूप में, स्विफ्ट बजाय पकड़ने और एक मूल्य की एक प्रति की दुकान करता है, तो उस मान से या बंद करने के बाहर उत्परिवर्तित नहीं है हो सकता है।

पहले स्निपेट में @Eonil द्वारा प्रश्न पोस्ट के रूप में, आपके पास बंद होने में Optional<AAA> का एक मजबूत संदर्भ है। हालांकि जब आप ए 1 से शून्य सेट करते हैं, तो आप वास्तव में क्या करते हैं, वैकल्पिक प्रकार के अंदर लपेटा गया एएए प्रकार के मूल्य को हटा रहा है।

जब आप a2 बंद करते हैं, तो आप उसी विकल्प को बिना किसी मूल्य के वापस लौटाते हैं, जो वास्तव में nil माध्यम है। इसलिए, एक मजबूत संदर्भ रखने का मतलब है कि आपका बंदरगाह a1 के समान वैकल्पिक मान साझा करता है। इसका मतलब यह नहीं है कि यह वैकल्पिक शून्य पर सेट नहीं किया जा सकता है।

अपने दूसरे कोड स्निपेट में, आपने कैप्चर सूची में a1 पर कब्जा कर लिया। इसका मतलब है कि आप a1 की प्रतिलिपि बनाते हैं और बंद होने के अंदर मूल्य a1 बंद होने के बाहर मूल्य a1 के साथ कुछ लेना देना नहीं है। तो यदि आप एक 1 से शून्य तक सेट करते हैं, तो यह बंद होने से आपको जो भी मिलता है उसे प्रभावित नहीं करता है।

मेरी अंग्रेजी अच्छी नहीं है और मुझे उम्मीद है कि मैंने अपनी राय स्पष्ट रूप से व्यक्त की है। या आप this article पढ़ सकते हैं, मुझे विश्वास है कि इससे आपको बहुत मदद मिलेगी।

संबंधित मुद्दे