हमेशा एआरसी में ब्लॉक में स्वयं का कमजोर संदर्भ पारित करें?


252

मैं ऑब्जेक्टिव-सी में ब्लॉक उपयोग के बारे में थोड़ा उलझन में हूं। मैं वर्तमान में ARC का उपयोग करता हूं और मेरे ऐप में मेरे पास बहुत सारे ब्लॉक हैं, वर्तमान selfमें इसके कमजोर संदर्भ के बजाय हमेशा संदर्भित करता है। हो सकता है कि इन ब्लॉकों को बनाए रखने selfऔर निस्तारण से रखने का कारण हो ? सवाल यह है कि क्या मुझे हमेशा एक ब्लॉक में एक weakसंदर्भ का उपयोग करना चाहिए self?

-(void)handleNewerData:(NSArray *)arr
{
    ProcessOperation *operation =
    [[ProcessOperation alloc] initWithDataToProcess:arr
                                         completion:^(NSMutableArray *rows) {
        dispatch_async(dispatch_get_main_queue(), ^{
            [self updateFeed:arr rows:rows];
        });
    }];
    [dataProcessQueue addOperation:operation];
}

ProcessOperation.h

@interface ProcessOperation : NSOperation
{
    NSMutableArray *dataArr;
    NSMutableArray *rowHeightsArr;
    void (^callback)(NSMutableArray *rows);
}

ProcessOperation.m

-(id)initWithDataToProcess:(NSArray *)data completion:(void (^)(NSMutableArray *rows))cb{

    if(self =[super init]){
        dataArr = [NSMutableArray arrayWithArray:data];
        rowHeightsArr = [NSMutableArray new];
        callback = cb;
    }
    return self;
}

- (void)main {
    @autoreleasepool {
        ...
        callback(rowHeightsArr);
    }
}

यदि आप इस विषय पर गहराई से प्रवचन चाहते हैं तो dhoerl.wordpress.com/2013/04/23/… पढ़े
डेविड एच।

जवाबों:


721

यह पर ध्यान केंद्रित करने में मदद करता है नहीं strongया weakचर्चा का हिस्सा है। इसके बजाय चक्र भाग पर ध्यान केंद्रित करें ।

एक रिट चक्र एक लूप है जो तब होता है जब ऑब्जेक्ट ए को ऑब्जेक्ट बी रखता है, और ऑब्जेक्ट बी ऑब्जेक्ट ए को बनाए रखता है। उस स्थिति में, यदि या तो जारी किया जाता है:

  • ऑब्जेक्ट A को नहीं हटाया जाएगा क्योंकि ऑब्जेक्ट B इसके लिए एक संदर्भ रखता है।
  • जब तक ऑब्जेक्ट A का संदर्भ है, तब तक ऑब्जेक्ट B को कभी नहीं हटाया जाएगा।
  • लेकिन ऑब्जेक्ट ए को कभी नहीं हटाया जाएगा क्योंकि ऑब्जेक्ट बी इसके लिए एक संदर्भ रखता है।
  • एड इन्फिटम

इस प्रकार, उन दो वस्तुओं को केवल कार्यक्रम के जीवन के लिए स्मृति में चारों ओर लटका दिया जाएगा, भले ही वे सब कुछ ठीक से काम कर रहे हों, निपटाए जाएं।

तो, हम जिस चीज के बारे में चिंतित हैं, वह चक्र बनाए रखना है , और इन चक्रों को बनाने वाले अपने आप में ब्लॉक के बारे में कुछ भी नहीं है। यह एक समस्या नहीं है, उदाहरण के लिए:

[myArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop){
   [self doSomethingWithObject:obj];
}];

ब्लॉक बरकरार है self, लेकिन ब्लॉक को बरकरार selfनहीं रखता है। यदि एक या दूसरे को छोड़ दिया जाता है, तो कोई भी चक्र नहीं बनता है और सबकुछ निपट जाता है जैसा कि इसे करना चाहिए।

जहाँ आप मुश्किल में पड़ जाते हैं, वह कुछ इस तरह है:

//In the interface:
@property (strong) void(^myBlock)(id obj, NSUInteger idx, BOOL *stop);

//In the implementation:
[self setMyBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  [self doSomethingWithObj:obj];     
}];

अब, आपकी वस्तु ( self) में strongब्लॉक का स्पष्ट संदर्भ है। और ब्लॉक का एक निहित मजबूत संदर्भ है self। यह एक चक्र है, और अब न तो वस्तु को ठीक से निपटाया जाएगा।

क्योंकि, इस तरह की स्थिति में, self परिभाषा के अनुसार पहले strongसे ही ब्लॉक का संदर्भ है, आमतौर पर selfब्लॉक का उपयोग करने के लिए स्पष्ट रूप से कमजोर संदर्भ बनाकर हल करना सबसे आसान है :

__weak MyObject *weakSelf = self;
[self setMyBlock:^(id obj, NSUInteger idx, BOOL *stop) {
  [weakSelf doSomethingWithObj:obj];     
}];

लेकिन यह डिफ़ॉल्ट पैटर्न नहीं होना चाहिए जिसे आप कॉल करने वाले ब्लॉक के साथ काम करते हैं self! इसका उपयोग केवल इस बात को तोड़ने के लिए किया जाना चाहिए कि क्या स्व और ब्लॉक के बीच एक चक्र होगा। यदि आप इस पैटर्न को हर जगह अपनाते हैं, तो आपको किसी ऐसी चीज़ से ब्लॉक करने का जोखिम होगा, जिसे selfनिपटा दिया गया था।

//SUSPICIOUS EXAMPLE:
__weak MyObject *weakSelf = self;
[[SomeOtherObject alloc] initWithCompletion:^{
  //By the time this gets called, "weakSelf" might be nil because it's not retained!
  [weakSelf doSomething];
}];

2
मुझे यकीन नहीं है कि ए बी, बी ए रिटेन अनंत चक्र करेगा। संदर्भ गणना के दृष्टिकोण से, A और B की संदर्भ संख्या है। 1. इस स्थिति के लिए एक चक्र बनाए रखने का क्या कारण है जब कोई अन्य समूह A और B के बाहर का मजबूत संदर्भ नहीं है - इसका मतलब है कि हम इन दो ऑब्जेक्ट तक नहीं पहुंच सकते (हम बी और इसके विपरीत रिलीज करने के लिए ए को नियंत्रित नहीं कर सकता), ए और बी एक दूसरे को खुद को जीवित रखने के लिए संदर्भ देते हैं।
दिन्युन लियू

@Danyun हालांकि यह सच है कि एक एक के बीच चक्र को बनाए रखने और बी नहीं है अप्राप्य है, जब तक इन वस्तुओं के लिए अन्य सभी संदर्भों को जारी किया गया है कि यह किसी भी कम एक चक्र नहीं है। इसके विपरीत, सिर्फ इसलिए कि एक विशेष चक्र पुनर्प्राप्त करने योग्य हो सकता है इसका मतलब यह नहीं है कि यह आपके कोड में होना ठीक है। खराब साइकल से बदबू आती है।
जेमोंस

@ जैममन्स हां, हमें हमेशा एक चक्र डिजाइन को बनाए रखने से बचना चाहिए जितना हम कर सकते हैं।
दिन्युन लियू

1
@ मैस्टर मेरे लिए कहना असंभव है। यह पूरी तरह से आपके -setCompleteionBlockWithSuccess:failure:तरीके के कार्यान्वयन पर निर्भर करता है । लेकिन अगर paginatorस्वामित्व में है ViewController, और इन ब्लॉकों को कॉल नहीं ViewControllerकिया जाएगा, तो रिहा होने के बाद , एक __weakसंदर्भ का उपयोग करके सुरक्षित कदम होगा (क्योंकि selfब्लॉक का मालिक होने वाली चीज़ का मालिक है, और इसलिए ब्लॉक के कॉल करने पर भी ऐसा होने की संभावना है। भले ही वे इसे बरकरार नहीं रखते)। लेकिन यह "अगर" का एक बहुत कुछ है। यह वास्तव में इस पर निर्भर करता है कि यह क्या करना चाहिए।
जेमोंस

1
@ जय नहीं, और यह ब्लॉक / क्लोजर के साथ स्मृति प्रबंधन समस्या के केंद्र में है। जब उनके पास कुछ नहीं होता तो वस्तुओं का सौदा हो जाता है। MyObjectऔर SomeOtherObjectदोनों ही ब्लॉक के मालिक हैं। लेकिन क्योंकि ब्लॉक का संदर्भ वापस MyObjectहै weak, ब्लॉक का मालिक नहीं हैMyObject । इसलिए जब ब्लॉक के रूप में लंबे समय के रूप में अस्तित्व की गारंटी है या तो MyObject या SomeOtherObject मौजूद हैं, तो कोई गारंटी नहीं है कि MyObjectजब तक ब्लॉक करता है के रूप में उपलब्ध नहीं होगा। MyObjectपूरी तरह से निपटा जा सकता है और, जब तक SomeOtherObjectअभी भी मौजूद है, तब भी ब्लॉक रहेगा।
जैम

26

आपको हमेशा एक कमजोर संदर्भ का उपयोग करने की आवश्यकता नहीं है। यदि आपका ब्लॉक बरकरार नहीं है, लेकिन निष्पादित और फिर खारिज कर दिया गया है, तो आप स्वयं को दृढ़ता से पकड़ सकते हैं, क्योंकि यह एक बनाए रखने के चक्र को नहीं बनाएगा। कुछ मामलों में, आप ब्लॉक को पूरा होने तक स्वयं को रोकना चाहते हैं ताकि यह समय से पहले न निपटे। यदि, हालांकि, आप ब्लॉक को जोरदार तरीके से कैप्चर करते हैं, और स्वयं को कैप्चर करते हैं, तो यह एक चक्र बनाए रखेगा।


वैसे मैं सिर्फ ब्लॉक को एक कॉलबैक के रूप में निष्पादित करता हूं और मैं स्वयं को बिल्कुल भी नहीं चाहता हूं। लेकिन ऐसा लगता है मानो मैं रिटेन साइकल बना रहा हूं क्योंकि विचाराधीन नियंत्रक से निपटा नहीं जाता है ...
the_citic

1
उदाहरण के लिए, यदि आपके पास एक बार बटन आइटम है जिसे व्यू कंट्रोलर द्वारा बरकरार रखा जाता है, और आप उस ब्लॉक में स्वयं को जोरदार तरीके से कैप्चर करते हैं, तो एक रिट चक्र होगा।
लियो नातन

1
@MartinE। आपको कोड नमूने के साथ अपने प्रश्न को अपडेट करना चाहिए। लियो काफी सही है (+1), कि यह जरूरी नहीं कि मजबूत संदर्भ चक्र का कारण बने, लेकिन यह इस बात पर निर्भर करता है कि आप इन ब्लॉकों का उपयोग कैसे करते हैं। यदि आप कोड स्निपेट प्रदान करते हैं, तो आपकी सहायता करना हमारे लिए आसान होगा।
रोब

@LeoNatan मैं चक्र को बनाए रखने की धारणा को समझता हूं, लेकिन मुझे बिल्कुल यकीन नहीं है कि ब्लॉकों में क्या होता है, ताकि मुझे थोड़ा भ्रमित किया जाए
the_critic

उपरोक्त कोड में, ऑपरेशन समाप्त होने और ब्लॉक जारी होने के बाद आपका आत्म उदाहरण जारी किया जाएगा। आपको यह पढ़ना चाहिए कि कैसे ब्लॉक काम करते हैं और कब और क्या वे अपने दायरे में कब्जा करते हैं।
लियो नातन

26

मैं पूरी तरह से @jemmons से सहमत हूँ:

लेकिन यह डिफ़ॉल्ट पैटर्न नहीं होना चाहिए जिसे आप उन ब्लॉकों से निपटते हैं जो स्वयं को कॉल करते हैं! इसका उपयोग केवल इस बात को तोड़ने के लिए किया जाना चाहिए कि क्या स्व और ब्लॉक के बीच एक चक्र होगा। यदि आप इस पैटर्न को हर जगह अपनाते हैं, तो आप ब्लॉक को किसी ऐसी चीज़ से पारित करने का जोखिम उठा सकते हैं, जिसे स्वयं समाप्त होने के बाद निष्पादित किया जाता है।

//SUSPICIOUS EXAMPLE:
__weak MyObject *weakSelf = self;
[[SomeOtherObject alloc] initWithCompletion:^{
  //By the time this gets called, "weakSelf" might be nil because it's not  retained!
  [weakSelf doSomething];
}];

इस समस्या को दूर करने के weakSelfलिए ब्लॉक के अंदर एक मजबूत संदर्भ को परिभाषित किया जा सकता है :

__weak MyObject *weakSelf = self;
[[SomeOtherObject alloc] initWithCompletion:^{
  MyObject *strongSelf = weakSelf;
  [strongSelf doSomething];
}];

3
कमजोर के लिए संदर्भ संख्या नहीं बढ़ाना होगा? इस प्रकार एक चक्र बनाए रखने?
mskw

12
यदि वे वस्तु के स्थिर अवस्था में मौजूद हों तो ही चक्र को बनाए रखें। जबकि कोड निष्पादित हो रहा है और इसका राज्य प्रवाह में है, कई और संभवतः अनावश्यक रिटेंशन ठीक हैं। इस पैटर्न के बारे में वैसे भी, एक मजबूत संदर्भ को कैप्चर करना, ब्लॉक रन से पहले स्वयं से निपटने के मामले के लिए कुछ भी नहीं करता है, जो अभी भी हो सकता है। यह सुनिश्चित करता है कि ब्लॉक को निष्पादित करते समय स्वयं को निपटाया न जाए । यह तब होता है जब ब्लॉक async कार्रवाई करता है कि होने के लिए एक खिड़की दे रही है।
पियरे ह्यूस्टन

@ एसएमलडक, आपका स्पष्टीकरण बहुत अच्छा है। अब मैं इसे बेहतर समझ रहा हूं। पुस्तकों ने इसे कवर नहीं किया, धन्यवाद।
Huibin Zhang

5
यह strongSelf का एक अच्छा उदाहरण नहीं है, क्योंकि strongSelf का स्पष्ट जोड़ वास्तव में रनटाइम वैसे भी होता है: doSomething लाइन पर, विधि कॉल की अवधि के लिए एक मजबूत संदर्भ लिया जाता है। अगर कमजोर पहले से ही अमान्य था, तो मजबूत रिफ शून्य है और विधि कॉल एक नो-ऑप है। जहाँ मजबूत करने में मदद मिलती है यदि आपके पास संचालन की एक श्रृंखला है, या एक सदस्य क्षेत्र ( ->) तक पहुंच है , जहां आप गारंटी देना चाहते हैं कि आपको वास्तव में एक वैध संदर्भ मिला है और इसे संचालन के पूरे सेट पर लगातार पकड़ रहा है, जैसेif ( strongSelf ) { /* several operations */ }
एथन

19

जैसा कि लियो बताते हैं, आपके द्वारा अपने प्रश्न में जोड़ा गया कोड एक मजबूत संदर्भ चक्र (उर्फ, चक्र को बनाए रखने) का सुझाव नहीं देगा। एक ऑपरेशन से संबंधित मुद्दा जो एक मजबूत संदर्भ चक्र का कारण बन सकता है यदि ऑपरेशन जारी नहीं हो रहा है। हालांकि आपका कोड स्निपेट बताता है कि आपने अपने ऑपरेशन को समवर्ती नहीं माना है, लेकिन अगर आपके पास है, तो यह जारी नहीं किया जाएगा यदि आप कभी पोस्ट नहीं करते हैं isFinished, या यदि आपके पास परिपत्र निर्भरता थी, या ऐसा कुछ है। और यदि ऑपरेशन जारी नहीं किया गया है, तो दृश्य नियंत्रक भी जारी नहीं किया जाएगा। मैं एक ब्रेकपॉइंट या NSLogआपके ऑपरेशन के deallocतरीके को जोड़ने का सुझाव दूंगा और पुष्टि करूंगा कि कॉल किया जा रहा है।

तुमने कहा था:

मैं चक्र बनाए रखने की धारणा को समझता हूं, लेकिन मुझे इस बात पर बिल्कुल यकीन नहीं है कि ब्लॉकों में क्या होता है, इसलिए यह मुझे थोड़ा भ्रमित करता है

ब्लॉक के साथ होने वाले चक्र (मजबूत संदर्भ चक्र) के मुद्दों को बनाए रखने के चक्र की तरह ही हैं जिनसे आप परिचित हैं। एक ब्लॉक किसी भी ऑब्जेक्ट के लिए मजबूत संदर्भ बनाए रखेगा जो ब्लॉक के भीतर दिखाई देता है, और यह उन मजबूत संदर्भों को तब तक जारी नहीं करेगा जब तक कि ब्लॉक स्वयं जारी नहीं हो जाता। इस प्रकार, यदि ब्लॉक संदर्भ self, या यहां तक ​​कि सिर्फ एक उदाहरण चर का selfसंदर्भ देता है, जो स्वयं के लिए मजबूत संदर्भ बनाए रखेगा, जो तब तक हल नहीं होता है जब तक कि ब्लॉक जारी नहीं किया जाता है (या इस मामले में, जब तक NSOperationउप-वर्ग जारी नहीं किया जाता है।

अधिक जानकारी के लिए, ऑब्जेक्टिव-सी के साथ प्रोग्रामिंग के सेल्फ सेक्शन को ब्लॉक करने के साथ मजबूत संदर्भ चक्र देखें : ब्लॉक दस्तावेज़ के साथ कार्य करना

यदि आपका व्यू कंट्रोलर अभी भी रिलीज़ नहीं हो रहा है, तो आपको बस यह पहचानना होगा कि अनसुलझे मज़बूत संदर्भ कहाँ रहते हैं (यह मानते हुए कि आपने पुष्टि की NSOperationहै कि डील हो रही है)। एक सामान्य उदाहरण एक दोहराव का उपयोग है NSTimer। या कुछ कस्टम delegateया अन्य ऑब्जेक्ट जो गलती से एक strongसंदर्भ बनाए रख रहा है । आप अक्सर साधनों को ट्रैक करने के लिए उपयोग कर सकते हैं जहां ऑब्जेक्ट्स को उनके मजबूत संदर्भ मिल रहे हैं, उदाहरण के लिए:

रिकॉर्ड संदर्भ Xcode 6 में गिना जाता है

या Xcode 5 में:

रिकॉर्ड संदर्भ Xcode 5 में गिना जाता है


1
एक अन्य उदाहरण यह होगा कि यदि ऑपरेशन को ब्लॉक क्रिएटर में रखा गया है और इसे समाप्त होने के बाद जारी नहीं किया जाएगा। अच्छा लिखने पर +1!
सिंह नटं

@LeoNatan सहमत, हालांकि कोड स्निपेट इसे एक स्थानीय चर के रूप में प्रस्तुत करता है जिसे एआरसी का उपयोग करने पर जारी किया जाएगा। लेकिन आप बिलकुल सही कह रहे हैं!
रोब

1
हां, मैं सिर्फ एक उदाहरण दे रहा था, जैसा कि अन्य उत्तर में ओपी ने अनुरोध किया था।
सिंह नटं

वैसे, Xcode 8 में "डिबग मेमोरी ग्राफ़" है, जो उन वस्तुओं के लिए मजबूत संदर्भ खोजने का एक आसान तरीका है जो जारी नहीं किए गए हैं। Stackoverflow.com/questions/30992338/… देखें ।
रोब

0

कुछ स्पष्टीकरण रिटेन चक्र के बारे में एक शर्त को नजरअंदाज करते हैं [यदि वस्तुओं का एक समूह मजबूत रिश्तों के एक चक्र से जुड़ा हुआ है, तो वे एक दूसरे को जीवित रखते हैं भले ही समूह के बाहर से कोई मजबूत संदर्भ न हो।] अधिक जानकारी के लिए, दस्तावेज़ पढ़ें।


-2

यह आप ब्लॉक के अंदर स्वयं का उपयोग कैसे कर सकते हैं:

// ब्लॉक की कॉलिंग

 NSString *returnedText= checkIfOutsideMethodIsCalled(self);

NSString* (^checkIfOutsideMethodIsCalled)(*)=^NSString*(id obj)
{
             [obj MethodNameYouWantToCall]; // this is how it will call the object 
            return @"Called";


};
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.