इस ब्लॉक में स्वयं को जोरदार तरीके से कैप्चर करने से चक्र को बनाए रखने की संभावना है


207

मैं इस चेतावनी को xcode में कैसे टाल सकता हूं। यहाँ कोड स्निपेट है:

[player(AVPlayer object) addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
queue:nil usingBlock:^(CMTime time) {
    current+=1;

    if(current==60)
    {
        min+=(current/60);
        current = 0;
    }

    [timerDisp(UILabel) setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];///warning occurs in this line
}];

क्या timerDispवर्ग पर एक संपत्ति है?
टिम

हां, @property (नॉनटॉमिक, मजबूत) UILabel * टाइमरडिप;
user1845209

2
यह क्या है: player(AVPlayer object)और timerDisp(UILabel)?
कार्ल वीज़ेई

AVPlayer * खिलाड़ी; यूलैबेल * टाइमरडिप;
user1845209

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

जवाबों:


514

selfयहाँ का कब्जा आपके निहित संपत्ति के उपयोग के साथ आ रहा है self.timerDisp- आप उस ब्लॉक के भीतर से selfसंपत्तियों या संपत्तियों का उल्लेख नहीं कर सकते हैं selfजिन्हें दृढ़ता से बनाए रखा जाएगा self

अपने ब्लॉक के अंदर selfपहुंचने से पहले एक कमजोर संदर्भ बनाकर आप इसे प्राप्त कर सकते हैं timerDisp:

__weak typeof(self) weakSelf = self;
[player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
                                     queue:nil
                                usingBlock:^(CMTime time) {
                                                current+=1;

                                                if(current==60)
                                                {
                                                    min+=(current/60);
                                                    current = 0;
                                                }

                                                 [weakSelf.timerDisp setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];
                                            }];

13
__unsafe_unretainedइसके बजाय प्रयोग करके देखें ।
टिम

63
संकल्प लिया। इसके बजाय इसका उपयोग करें: __unsafe_unretain typeof (self) weakSelf = self; मदद के लिए धन्यवाद
@Tim

1
अच्छा जवाब है, लेकिन मैं आपके साथ यह कहकर छोटा मुद्दा लेता हूं: "आप एक ब्लॉक के भीतर से स्वयं पर या स्वयं के गुणों का उल्लेख नहीं कर सकते हैं जो स्वयं द्वारा दृढ़ता से बनाए रखा जाएगा।" यह कड़ाई से सच नहीं है। कृपया मेरा जवाब नीचे देखें। कहने के लिए बेहतर है, "आपको बहुत ध्यान रखना चाहिए अगर आप स्वयं को देखें ..."
क्रिस सोर्ट

8
मुझे ओपी कोड में एक अनुरक्षण चक्र नहीं दिखता है। ब्लॉक को दृढ़ता से बनाए नहीं रखा गया है self, यह मुख्य प्रेषण कतार द्वारा बनाए रखा गया है। क्या मै गलत हु?
एरिकप्रिस

3
@erikprice: आप गलत नहीं हैं। मैंने प्रश्न की व्याख्या मुख्य रूप से त्रुटि Xcode प्रस्तुत करने के बारे में की है ("मैं Xcode में इस चेतावनी से कैसे बच सकता हूं"), बजाय एक बनाए चक्र की वास्तविक उपस्थिति के बारे में। आप सही कह रहे हैं कि कोई भी चक्र बरकरार नहीं है, केवल स्निपेट ओपी द्वारा प्रदान किया गया है।
टिम

52
__weak MyClass *self_ = self; // that's enough
self.loadingDidFinishHandler = ^(NSArray *receivedItems, NSError *error){
    if (!error) {
       [self_ showAlertWithError:error];
    } else {
       self_.items = [NSArray arrayWithArray:receivedItems];
       [self_.tableView reloadData];
    }
};

और एक बहुत महत्वपूर्ण बात याद रखें: सीधे ब्लॉक में उदाहरण चर का उपयोग न करें, इसे कमजोर वस्तु के गुण के रूप में उपयोग करें, नमूना:

self.loadingDidFinishHandler = ^(NSArray *receivedItems, NSError *error){
        if (!error) {
           [self_ showAlertWithError:error];
        } else {
           self_.items = [NSArray arrayWithArray:receivedItems];
           [_tableView reloadData]; // BAD! IT ALSO WILL BRING YOU TO RETAIN LOOP
        }
 };

और करना मत भूलना:

- (void)dealloc {
    self.loadingCompletionHandler = NULL;
}

एक और मुद्दा दिखाई दे सकता है यदि आप किसी भी वस्तु द्वारा बनाए नहीं रखने की कमजोर प्रतिलिपि पास करेंगे:

MyViewController *vcToGo = [[MyViewCOntroller alloc] init];
__weak MyViewController *vcToGo_ = vcToGo;
self.loadingCompletion = ^{
    [vcToGo_ doSomePrecessing];
};

अगर vcToGoनिपटा जाएगा और फिर यह ब्लॉक निकाल दिया गया तो मेरा मानना ​​है कि आप बिना किसी पहचाने हुए चयनकर्ता के साथ दुर्घटनाग्रस्त हो जाएंगे, जिसमें vcToGo_अब चर है। इसे नियंत्रित करने की कोशिश करें।


3
यह एक मजबूत जवाब होगा यदि आप भी इसे समझाते हैं।
एरिक जे।

43

बेहतर संस्करण

__strong typeof(self) strongSelf = weakSelf;

अपने ब्लॉक में पहली पंक्ति के रूप में उस कमजोर संस्करण का एक मजबूत संदर्भ बनाएं। यदि स्वयं अभी भी मौजूद है जब ब्लॉक निष्पादित करना शुरू कर देता है और शून्य पर वापस नहीं गिरता है, तो यह रेखा यह सुनिश्चित करती है कि यह ब्लॉक के निष्पादन जीवन भर बनी रहती है।

तो पूरी बात इस तरह होगी:

// Establish the weak self reference
__weak typeof(self) weakSelf = self;

[player addPeriodicTimeObserverForInterval:CMTimeMakeWithSeconds(0.1, 100)
                                 queue:nil
                            usingBlock:^(CMTime time) {

    // Establish the strong self reference
    __strong typeof(self) strongSelf = weakSelf;

    if (strongSelf) {
        [strongSelf.timerDisp setText:[NSString stringWithFormat:@"%02d:%02d",min,current]];
    } else {
        // self doesn't exist
    }
}];

मैंने इस लेख को कई बार पढ़ा है। यह द्वारा एक उत्कृष्ट लेख है एरिका Sadun पर कैसे मुद्दों से बचने के उपयोग करते हुए जब ब्लॉक और NSNotificationCenter


स्विफ्ट अपडेट:

उदाहरण के लिए, सफलता ब्लॉक के साथ एक सरल विधि को तेज करना होगा:

func doSomeThingWithSuccessBlock(success: () -> ()) {
    success()
}

जब हम इस विधि को कहते हैं और selfसफलता ब्लॉक में उपयोग करने की आवश्यकता होती है । हम सुविधाओं [weak self]और guard letसुविधाओं का उपयोग करेंगे ।

    doSomeThingWithSuccessBlock { [weak self] () -> () in
        guard let strongSelf = self else { return }
        strongSelf.gridCollectionView.reloadData()
    }

यह तथाकथित मजबूत-कमजोर नृत्य लोकप्रिय ओपन सोर्स प्रोजेक्ट द्वारा उपयोग किया जाता है Alamofire

अधिक जानकारी के लिए स्विफ्ट-स्टाइल-गाइड देखें


क्या होगा अगर आपने typeof(self) strongSelf = self;ब्लॉक के बाहर किया (__weak के बजाय) तो ब्लॉक में strongSelf = nil;उपयोग के बाद कहा ? मैं यह नहीं देखता कि आपका उदाहरण यह सुनिश्चित करता है कि ब्लॉक निष्पादित होने तक कमजोर नहीं है।
मैट

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

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

15

एक अन्य जवाब में, टिम ने कहा:

आप किसी ऐसे ब्लॉक के भीतर से स्वयं पर या स्वयं के गुणों का उल्लेख नहीं कर सकते हैं जो स्वयं द्वारा दृढ़ता से बनाए रखा जाएगा।

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

मेरे मामले में अभी, मेरे पास कोड के लिए यह चेतावनी थी:

[x setY:^{ [x doSomething]; }];

अब मुझे पता है कि यह क्लैंग केवल इस चेतावनी का उत्पादन करेगा यदि यह पता लगाता है कि यह विधि "सेट" (और एक अन्य विशेष मामला जिसे मैं यहां उल्लेख नहीं करूंगा) से शुरू होता है। मेरे लिए, मुझे पता है कि वहाँ एक लूप बनाए रखने का कोई खतरा नहीं है, इसलिए मैंने विधि का नाम बदलकर "यूटीवाई:" रखा, बेशक, यह सभी मामलों में उचित नहीं होगा और आमतौर पर आप एक कमजोर संदर्भ का उपयोग करना चाहेंगे, लेकिन मैंने सोचा कि यह मेरे समाधान को ध्यान देने योग्य है, क्योंकि यह दूसरों की मदद करता है।


4

कई बार, यह वास्तव में एक चक्र नहीं है

यदि आप जानते हैं कि यह नहीं है, तो आपको दुनिया में फलहीन कमजोरियों को लाने की जरूरत नहीं है।

ऐप्पल ने हमें इन चेतावनियों को उनके साथ एपीआई के लिए बाध्य किया है UIPageViewController, जिसमें एक सेट विधि भी शामिल है (जो इन चेतावनियों को ट्रिगर करता है - जैसा कि कहीं और उल्लेख किया गया है - आप एक आइवर को एक मान सेट कर रहे हैं, जो एक ब्लॉक है) और एक पूरा हैंडलर ब्लॉक (जिसमें आप निस्संदेह अपने आप को देखें)।

कोड की एक पंक्ति से चेतावनी निकालने के लिए कुछ संकलक निर्देश यहां दिए गए हैं:

#pragma GCC diagnostic push
#pragma clang diagnostic ignored "-Warc-retain-cycles"
    [self.pageViewController setViewControllers:@[newViewController] direction:navigationDirection animated:YES completion:^(BOOL finished) {
        // this warning is caused because "setViewControllers" starts with "set…", it's not a problem
        [self doTheThingsIGottaDo:finished touchThePuppetHead:YES];
    }];
#pragma GCC diagnostic pop

1

परिशुद्धता और शैली में सुधार पर दो सेंट जोड़ना। ज्यादातर मामलों में आप selfइस ब्लॉक में केवल एक या दो सदस्यों का उपयोग करेंगे , सबसे अधिक संभावना सिर्फ एक स्लाइडर को अपडेट करने के लिए। कास्टिंग selfओवरकिल है। इसके बजाय, स्पष्ट होना बेहतर है और केवल उन वस्तुओं को डालें जिन्हें आपको वास्तव में ब्लॉक के अंदर की आवश्यकता है। उदाहरण के लिए, यदि यह उदाहरण है UISlider*, कहो _timeSlider, बस ब्लॉक घोषणा से पहले निम्न कार्य करें:

UISlider* __weak slider = _timeSlider;

फिर sliderब्लॉक के अंदर ही उपयोग करें । तकनीकी रूप से यह अधिक सटीक है क्योंकि यह संभावित बनाए रखने वाले चक्र को केवल उस वस्तु तक ले जाता है जिसकी आपको जरूरत है, न कि सभी वस्तुओं को self

पूर्ण उदाहरण:

UISlider* __weak slider = _timeSlider;
[_embeddedPlayer addPeriodicTimeObserverForInterval:CMTimeMake(1, 1)
     queue:nil
     usingBlock:^(CMTime time){
        slider.value = time.value/time.timescale;
     }
];

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

@property (nonatomic, weak) IBOutlet UISlider* timeSlider;

कोडिंग शैली के संदर्भ में, सी और सी ++ के साथ, चर घोषणाएं सही से बाईं ओर पढ़ी जाती हैं। SomeType* __weak variableइस क्रम में घोषणा करने से दाहिनी से बाईं ओर अधिक स्वाभाविक रूप से पढ़ता है variable is a weak pointer to SomeType:।


1

मैं हाल ही में इस चेतावनी में भाग गया और इसे थोड़ा बेहतर समझना चाहता था। थोड़ा परीक्षण और त्रुटि के बाद, मुझे पता चला कि यह "ऐड" या "सेव" के साथ एक विधि शुरू करने से उत्पन्न होता है। ऑब्जेक्टिव C "नया", "आबंटित", आदि के साथ शुरू किए गए विधि नाम को एक रखी हुई वस्तु को वापस करता है, लेकिन "ऐड" या "सेव" के बारे में कुछ भी उल्लेख नहीं करता है। हालाँकि, अगर मैं इस तरह से एक विधि नाम का उपयोग करता हूं:

[self addItemWithCompletionBlock:^(NSError *error) {
            [self done]; }];

मैं [आत्म किया] लाइन पर चेतावनी देखूंगा। हालांकि, यह नहीं होगा:

[self itemWithCompletionBlock:^(NSError *error) {
    [self done]; }];

मैं आगे जाऊंगा और अपनी वस्तु को संदर्भित करने के लिए "__weak __typeof (self) weakSelf = self" तरीके का उपयोग करूंगा लेकिन वास्तव में ऐसा करना पसंद नहीं है क्योंकि यह भविष्य में मुझे और / या अन्य देव को भ्रमित करेगा। बेशक, मैं "ऐड" (या "सेव") का भी उपयोग नहीं कर सकता था, लेकिन यह बदतर है क्योंकि यह विधि का अर्थ निकाल लेता है।

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