जब आप एक विधि पैरामीटर के रूप में ब्लॉक प्राप्त करते हैं, तो वह ब्लॉक मूल हो सकता है जो ढेर पर बनाया गया था या यह प्रतिलिपि (ढेर पर एक ब्लॉक) हो सकता है। जहां तक मुझे पता है, वहां कोई रास्ता नहीं है। तो अंगूठे का सामान्य नियम यह है कि यदि आप इसे प्राप्त करने वाली विधि के भीतर ब्लॉक निष्पादित करने जा रहे हैं, तो आपको इसकी प्रतिलिपि बनाने की आवश्यकता नहीं है। यदि आप उस ब्लॉक को किसी अन्य विधि से पास करना चाहते हैं (जो इसे तुरंत निष्पादित कर सकता है या नहीं) तो आपको इसे कॉपी करने की भी आवश्यकता नहीं है (प्राप्त करने की विधि को प्रतिलिपि बनाना चाहिए यदि वह इसे आसपास रखना चाहता है)। हालांकि, अगर आप ब्लॉक को बाद में निष्पादन के लिए किसी भी स्थान पर स्टोर करना चाहते हैं, तो आपको इसे कॉपी करने की आवश्यकता है।,
typedef void (^IDBlock) (id);
@implementation MyClass{
IDBlock _completionBlock;
}
हालांकि आप भी अगर तुम संग्रह वर्ग के किसी भी प्रकार में जोड़ने के लिए, एक तरह जा रहे हैं इसे कॉपी करने की जरूरत है: प्राथमिक उदाहरण बहुत से लोगों का उपयोग एक उदाहरण चर के रूप में आयोजित पूरा होने के ब्लॉक के कुछ प्रकार है एनएसएआरएआरई या एनएस डिक्शनरी। अन्यथा, जब आप बाद में ब्लॉक निष्पादित करने का प्रयास करेंगे तो आपको त्रुटियां (संभवतः EXC_BAD_ACCESS) या संभावित रूप से डेटा भ्रष्टाचार मिलेगा।
जब आप कोई ब्लॉक निष्पादित करते हैं, तो ब्लॉक को nil
पर पहले परीक्षण करना महत्वपूर्ण है। उद्देश्य-सी आपको ब्लॉक विधि पैरामीटर में nil
पास करने की अनुमति देगा। यदि वह ब्लॉक शून्य है, तो आप इसे निष्पादित करने का प्रयास करते समय EXC_BAD_ACCESS प्राप्त करेंगे। सौभाग्य से यह करना आसान है। अपने उदाहरण में, आप लिखना चाहते हैं:
- (void)testWithBlock:(void (^)(NSString *))block {
NSString *testString = @"Test";
if (block) block(testString);
}
नकल ब्लॉकों में प्रदर्शन विचार कर रहे हैं। ढेर पर एक ब्लॉक बनाने की तुलना में, एक ब्लॉक को ढेर में कॉपी करना तुच्छ नहीं है। यह सामान्य रूप से एक बड़ा सौदा नहीं है, लेकिन यदि आप इसे अवरुद्ध रूप से ब्लॉक का उपयोग कर रहे हैं या ब्लॉक के समूह का उपयोग कर रहे हैं और प्रत्येक निष्पादन पर उन्हें कॉपी कर रहे हैं, तो यह एक प्रदर्शन हिट बनाएगा। इसलिए यदि आपकी विधि - (void)testWithBlock:(void (^)(NSString *))block;
किसी प्रकार की लूप में थी, तो उस ब्लॉक को कॉपी करने से आपको अपने प्रदर्शन को नुकसान पहुंचा सकता है यदि आपको इसकी प्रतिलिपि बनाने की आवश्यकता नहीं है।
एक ब्लॉक को कॉपी करने के लिए आपको एक और जगह है यदि आप उस ब्लॉक को स्वयं कॉल करना चाहते हैं (ब्लॉक रिकर्सन)। यह सब सामान्य नहीं है, लेकिन यदि आप ऐसा करना चाहते हैं तो आपको ब्लॉक की प्रतिलिपि बनाना होगा। SO पर मेरा प्रश्न/उत्तर यहां देखें: Recursive Blocks In Objective-C।
अंत में, यदि आप एक ब्लॉक स्टोर करने जा रहे हैं तो आपको बनाए रखने वाले चक्र बनाने के बारे में वास्तव में सावधान रहना होगा। ब्लॉक किसी भी ऑब्जेक्ट को इसमें बनाए रखेंगे, और यदि वह ऑब्जेक्ट एक आवृत्ति चर है, तो यह आवृत्ति चर के वर्ग (स्वयं) को बनाए रखेगा। मैं व्यक्तिगत रूप से ब्लॉक प्यार करता हूं और हर समय उनका उपयोग करता हूं। लेकिन एक कारण है कि ऐप्पल अपने UIKit वर्गों के लिए ब्लॉक का उपयोग/स्टोर नहीं करता है और इसके बजाय लक्ष्य/क्रिया या प्रतिनिधि पैटर्न के साथ चिपक जाता है। यदि आप (ब्लॉक बनाने वाली कक्षा) उस वर्ग को बनाए रखते हैं जो ब्लॉक प्राप्त/प्रतिलिपि/संग्रहित कर रही है, और उस ब्लॉक में आप या तो स्वयं या किसी भी वर्ग आवृत्ति चर का संदर्भ लेते हैं, तो आपने एक बनाए रखा चक्र बनाया है (कक्षा ए -> कक्षाबी - > ब्लॉक -> कक्षा ए)। यह करना उल्लेखनीय रूप से आसान है, और यह कुछ ऐसा है जो मैंने कई बार किया है। इसके अलावा, इंस्ट्रूमेंट्स में "लीक्स" इसे पकड़ नहीं लेता है। इसके चारों ओर जाने का तरीका आसान है: बस एक अस्थायी __weak
चर (एआरसी के लिए) या __block
चर (गैर-एआरसी) बनाएं और ब्लॉक उस चर को बनाए रखेगा।
[object testWithBlock:^(NSString *test){
_iVar = test;
NSLog(@"[%@]", test);
}];
बहरहाल, यह (एआरसी का प्रयोग करके) ठीक करने के लिए:: तो, उदाहरण के लिए, निम्नलिखित एक चक्र को बनाए रखने अगर 'वस्तु' प्रतियां/दुकानों ब्लॉक होगा
__weak IVarClass *iVar = _iVar;
[object testWithBlock:^(NSString *test){
iVar = test;
NSLog(@"[%@]", test);
}];
आप भी कर सकते हैं कुछ इस तरह करते हैं:
__weak ClassOfSelf _self = self;
[object testWithBlock:^(NSString *test){
_self->_iVar = test;
NSLog(@"[%@]", test);
}];
नोट है कि कई लोगों के ऊपर पसंद नहीं है क्योंकि वे इसे कमजोर करने पर विचार, लेकिन यह चर तक पहुँचने का एक मान्य तरीका है। अद्यतन - वर्तमान कंपाइलर अब चेतावनी देता है कि यदि आप सीधे '->' का उपयोग कर चर का उपयोग करने का प्रयास करते हैं। इस कारण से (सुरक्षा के कारण भी) उन चरों के लिए एक संपत्ति बनाना सर्वोत्तम है, जिन्हें आप एक्सेस करना चाहते हैं। तो _self->_iVar = test;
के बजाय आप इसका उपयोग करेंगे: _self.iVar = test;
।
अद्यतन (अधिक जानकारी)
आम तौर पर, यह विधि है कि निर्धारित करने ब्लॉक फोन करने वाले के बजाय कॉपी किया जा करने की जरूरत है कि क्या के लिए जिम्मेदार के रूप में ब्लॉक प्राप्त करता है पर विचार करने के लिए सबसे अच्छा है। ऐसा इसलिए है क्योंकि प्राप्त करने वाली विधि केवल एकमात्र व्यक्ति हो सकती है जो जानता है कि ब्लॉक को कितनी देर तक जिंदा रखा जाना चाहिए या यदि इसकी प्रतिलिपि बनाने की आवश्यकता है। जब आप कॉल लिखते हैं तो आप (प्रोग्रामर के रूप में) इस जानकारी को स्पष्ट रूप से जान लेंगे, लेकिन यदि आप मानसिक रूप से कॉलर और रिसीवर को अलग-अलग ऑब्जेक्ट्स पर विचार करते हैं, तो कॉलर रिसीवर को ब्लॉक देता है और इसके साथ किया जाता है। इसलिए, इसे जानने के बाद ब्लॉक के साथ क्या किया जाना चाहिए, यह जानने की आवश्यकता नहीं है। फ्लिप पक्ष पर, यह काफी संभव है कि कॉलर पहले से ही ब्लॉक की प्रतिलिपि बना सकता है (शायद यह ब्लॉक को संग्रहीत कर सकता है और अब इसे किसी अन्य विधि से सौंप रहा है) लेकिन रिसीवर (जो ब्लॉक को संग्रहीत करने का भी इरादा रखता है) अभी भी ब्लॉक की प्रतिलिपि बनाना चाहिए (भले ही ब्लॉक पहले से ही कॉपी किया गया है)। रिसीवर यह नहीं जान सकता कि ब्लॉक की पहले से ही प्रतिलिपि बनाई जा चुकी है, और कुछ ब्लॉक इसे प्राप्त किए जा सकते हैं जबकि अन्य नहीं हो सकते हैं। इसलिए रिसीवर को हमेशा एक ब्लॉक की प्रतिलिपि बनाना चाहिए जो इसे रखने के इरादे से है? सही बात? यह अनिवार्य रूप से अच्छा ऑब्जेक्ट ओरिएंटेड डिजाइन प्रथाओं है। असल में, जिनके पास जानकारी है, उन्हें संभालने के लिए जिम्मेदार है।
ब्लैक का उपयोग ऐप्पल के जीसीडी (ग्रांड सेंट्रल डिस्पैच) में बड़े पैमाने पर बहु-थ्रेडिंग को आसानी से करने के लिए किया जाता है। आम तौर पर, जब आप इसे जीसीडी पर भेजते हैं तो आपको ब्लॉक की प्रतिलिपि बनाने की आवश्यकता नहीं होती है। विचित्र रूप से, यह थोड़ा सा अंतर्ज्ञानी है (यदि आप इसके बारे में सोचते हैं) क्योंकि यदि आप एक ब्लॉक को असीमित रूप से प्रेषित करते हैं, तो ब्लॉक को निष्पादित करने से पहले ब्लॉक में जिस तरह से ब्लॉक बनाया गया था, आमतौर पर इसका मतलब यह होगा कि ब्लॉक समाप्त होने के बाद से समाप्त हो जाएगा एक ढेर वस्तु। मुझे नहीं लगता कि जीसीडी ब्लॉक को ढेर में कॉपी करता है (मैंने इसे कहीं पढ़ा है लेकिन इसे फिर से ढूंढने में असमर्थ रहा है), इसके बजाय मुझे लगता है कि धागे का जीवन एक और थ्रेड पर रखा जा रहा है।
आप एआरसी का उपयोग कर रहे हैं? –
@pcperini, हाँ। – rid