এই ব্লকে নিজেকে দৃ strongly়ভাবে ক্যাপচার করা বজায় রাখার চক্রের দিকে পরিচালিত করে


207

এক্সকোডে কীভাবে আমি এই সতর্কতাটি এড়াতে পারি। কোড স্নিপেট এখানে:

[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ক্লাসের মধ্যে সম্পত্তি?
টিম

হ্যাঁ, @ প্রপার্টি (ননোটমিক, স্ট্রং) ইউআইএলবেল * টাইমারডিস্প;
ব্যবহারকারী 1845209

2
এটি কি: player(AVPlayer object)এবং timerDisp(UILabel)?
কার্ল ভিয়েজি

এভিপ্লেয়ার * প্লেয়ার; ইউআইএলবেল * টাইমারডিস্প;
ব্যবহারকারী 1845209

5
আসল প্রশ্নটি হ'ল এই সতর্কতাটি কীভাবে নিজের উপর অপ্রয়োজনীয় দুর্বল রেফারেন্স ছাড়াই নিরব করা যায় , যখন আপনি জানেন যে বিজ্ঞপ্তিটি উল্লেখটি নষ্ট হয়ে যাবে (উদাহরণস্বরূপ, যদি কোনও নেটওয়ার্কের অনুরোধ শেষ হয়ে যায় আপনি যদি সর্বদা রেফারেন্সটি সাফ করেন)।
গ্লেন মেইনার্ড 21

উত্তর:


514

এখানকার ক্যাপচারটি selfআপনার অন্তর্নিহিত সম্পত্তি অ্যাক্সেসের সাথে চলেছে self.timerDisp- আপনি কোনও ব্লকের মধ্যে থেকে উল্লেখ করতে পারবেন না selfবা এমন বৈশিষ্ট্য selfযা দৃ retain়ভাবে ধরে রাখা হবে 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_unretained টাইপফ (স্ব) দুর্বল Self = স্ব; সহায়তার জন্য ধন্যবাদ
@

1
উত্তম উত্তর, তবে আমি আপনাকে এই বলে ছোট ছোট বিষয় নিয়েছি: "আপনি কোনও ব্লকের মধ্যে থেকে নিজের বা নিজের বৈশিষ্ট্যগুলিকে উল্লেখ করতে পারবেন না যা স্বাবলম্বভাবে দৃ retain়ভাবে ধরে রাখা হবে” " এটি কঠোরভাবে সত্য নয়। নীচে আমার উত্তর দেখুন। বলা ভাল, "আপনি যদি নিজেকে উল্লেখ করেন তবে আপনাকে অবশ্যই খুব যত্নবান হতে হবে ..."
ক্রিস সুটার

8
আমি ওপির কোডটিতে একটি রক্ষণশীল চক্র দেখতে পাচ্ছি না। ব্লকটি দৃ strongly়ভাবে ধরে রাখা যায় না self, এটি মূল প্রেরণের সারি দ্বারা ধরে রাখা হয়। আমি কি ভূল?
এরিকপ্রাইস

3
@ এরিকপ্রাইস: আপনি ভুল নন আমি প্রশ্নটি মূলত এক্সকোড উপস্থাপিত ত্রুটি ("কীভাবে আমি এক্সকোডে এই সতর্কতাটি এড়াতে পারি") রক্ষা চক্রের প্রকৃত উপস্থিতি সম্পর্কে নয় তার ব্যাখ্যা দিয়েছিলাম। আপনি ঠিক বলেছেন যে কোনও রক্ষণাবেক্ষণের চক্রটি কেবলমাত্র সরবরাহ করা স্নিপেট ওপি থেকেই স্পষ্ট হয় না।
টিম

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;ব্যবহারের পরে বলেছে ? আমি দেখতে পাচ্ছি না যে আপনার উদাহরণটি কীভাবে নিশ্চিত করে যে ব্লকটি কার্যকর হওয়ার সময় দুর্বল হয়ে গেছে।
ম্যাট

সম্ভাব্য ধরে রাখার চক্র এড়ানোর জন্য, আমরা কোনও কোডের বাইরে নিজের ব্যবহার করে এমন একটি ব্লকের বাইরে দুর্বল স্ব-রেফারেন্স স্থাপন করি। আপনার পথে, আপনাকে অবশ্যই নিশ্চিত করতে হবে যে ব্লকটি কার্যকর হয়েছে। আপনার কোডের আর একটি ব্লক এখন আপনার পূর্বের রক্ষণাবেক্ষণ মেমরি মুক্ত করার জন্য দায়বদ্ধ।
ওয়ারিফ আখন্দ ishষি

@ ম্যাট এই উদাহরণটির উদ্দেশ্য দুর্বল ব্যক্তিদের ধরে রাখা নয়। উদ্দেশ্যটি হ'ল, দুর্বলরা যদি শূন্য না হয় তবে ব্লকের অভ্যন্তরে একটি শক্ত রেফারেন্স তৈরি করুন। সুতরাং একবার ব্লকটি নিজের সাথে সম্পাদন করা শুরু করার পরে, ব্লকের অভ্যন্তরে স্বনিষ্ঠ হয়ে ওঠে না।
ওয়ারিফ আখন্দ ishষি

15

অন্য উত্তরে টিম বলেছিল:

আপনি কোনও ব্লকের মধ্যে থেকে স্ব বা নিজের বৈশিষ্ট্যগুলিকে উল্লেখ করতে পারবেন না যা নিজের দ্বারা দৃ strongly়ভাবে ধরে রাখা হবে।

এটি মোটেই সত্য নয়। আপনি যখন কোনও পর্যায়ে চক্রটি ভাঙ্গেন ততক্ষণ এটি করা আপনার পক্ষে ঠিক। উদাহরণস্বরূপ, আসুন আমরা বলি যে আপনার একটি টাইমার রয়েছে যা আগুনের সাথে একটি ব্লক রয়েছে যা নিজেকে ধরে রাখে এবং আপনি নিজেও টাইমারটির একটি দৃ reference় উল্লেখ রাখেন। এটি পুরোপুরি ঠিক আছে যদি আপনি সর্বদা জানেন যে আপনি কোনও সময় টাইমারটি ধ্বংস করবেন এবং চক্রটি ভেঙে ফেলবেন।

আমার ক্ষেত্রে এখনই, কোডের জন্য আমার এই সতর্কতা ছিল যা করেছিল:

[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ওভারকিল is পরিবর্তে, এটা ভালো স্পষ্টভাবে ব্যক্ত করতে এবং কাস্ট শুধুমাত্র বস্তু যা আপনি সত্যিই ব্লক ভিতরে প্রয়োজন। উদাহরণস্বরূপ, এটির উদাহরণস্বরূপ 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;

কোডিং স্টাইলের ক্ষেত্রে, সি এবং সি ++ এর মতো, ভেরিয়েবল ঘোষণাগুলি ডান থেকে বামে আরও ভালভাবে পড়তে পারে are ঘোষণা SomeType* __weak variableএই আদেশ সঠিক কাছ থেকে আরো স্বাভাবিকভাবেই সার্চ যেমন বাম: variable is a weak pointer to SomeType


1

আমি সম্প্রতি এই সতর্কতার মধ্যে দৌড়েছি এবং এটি আরও ভালভাবে বুঝতে চেয়েছিলাম understand কিছুটা পরীক্ষা এবং ত্রুটির পরে, আমি আবিষ্কার করেছি যে এটি "অ্যাড" বা "সেভ" দিয়ে কোনও পদ্ধতি শুরু থেকে উদ্ভূত। অবজেক্টিভ সি পদ্ধতি ব্যবহার করে নামগুলি "নতুন", "বরাদ্দ" ইত্যাদির সাথে শুরু করে যেমন কোনও রক্ষণাবেক্ষণ করা অবজেক্ট ফিরিয়ে দেয় তবে "অ্যাড" বা "সংরক্ষণ" সম্পর্কে কিছুই উল্লেখ করেনি (যে আমি খুঁজে পেতে পারি)। তবে, যদি আমি এই পদ্ধতিতে কোনও পদ্ধতির নাম ব্যবহার করি:

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

আমি [স্ব সম্পন্ন] লাইনে সতর্কতাটি দেখতে পাচ্ছি। তবে এটি করবে না:

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

আমি এগিয়ে যাব এবং আমার অবজেক্টের রেফারেন্স দেওয়ার জন্য "__weak __typeof (self) দুর্বল সেলফ = স্ব" উপায়টি ব্যবহার করব তবে সত্যিই তা করতে পছন্দ করি না কারণ এটি আমার এবং / বা অন্যান্য দেবতাকে বিভ্রান্ত করবে। অবশ্যই, আমি "অ্যাড" (বা "সংরক্ষণ") ব্যবহার করতে পারি না তবে এটি আরও খারাপ কারণ এটি পদ্ধতির অর্থ হরণ করে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.