capturing self strongly in this block is likely to lead to a retain cycle

Objective CCocoa TouchAutomatic Ref-CountingAvplayerRetain

Objective C Problem Overview


How can I avoid this warning in xcode. Here is the code snippet:

[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
}];

Objective C Solutions


Solution 1 - Objective C

The capture of self here is coming in with your implicit property access of self.timerDisp - you can't refer to self or properties on self from within a block that will be strongly retained by self.

You can get around this by creating a weak reference to self before accessing timerDisp inside your block:

__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]];
                                            }];

Solution 2 - Objective C

__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];
    }
};

And one very important thing to remember: do not use instance variables directly in block, use it as a properties of weak object, sample:

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
        }
 };

and don't forget to do:

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

another issue can appear if you will pass weak copy of not retained by anybody object:

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

if vcToGo will be deallocated and then this block fired I believe you will get crash with unrecognized selector to a trash which is contains vcToGo_ variable now. Try to control it.

Solution 3 - Objective C

Better version

__strong typeof(self) strongSelf = weakSelf;

> Create a strong reference to that weak version as the first line in your block. If self still exists when the block starts to execute and hasn’t fallen back to nil, this line ensures it persists throughout the block’s execution lifetime.

So the whole thing would be like this:

// 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
    }
}];

I have read this article many times. This is an excellent article by Erica Sadun on [How To Avoid Issues When Using Blocks And NSNotificationCenter][1]

[1]: http://www.informit.com/articles/article.aspx?p=2207347 "How To Avoid Issues When Using Blocks And NSNotificationCenter"


Swift update:

For example, in swift a simple method with success block would be:

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

When we call this method and need to use self in the success block. We'll be using the [weak self] and guard let features.

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

This so-called strong-weak dance is used by popular open source project Alamofire.

For more info check out swift-style-guide

Solution 4 - Objective C

In another answer, Tim said:

> you can't refer to self or properties on self from within a block that will be strongly retained by self.

This isn’t quite true. It’s OK for you to do this so long as you break the cycle at some point. For example, let’s say you have a timer that fires that has a block that retains self and you also keep a strong reference to the timer in self. This is perfectly fine if you always know that you will destroy the timer at some point and break the cycle.

In my case just now, I had this warning for code that did:

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

Now I happen to know that clang will only produce this warning if it detects the method starts with “set” (and one other special case that I won’t mention here). For me, I know there is no danger of there being a retain loop, so I changed the method name to “useY:” Of course, that might not be appropriate in all cases and usually you will want to use a weak reference, but I thought it worth noting my solution in case it helps others.

Solution 5 - Objective C

Many times, this is not actually a retain cycle.

If you know that it's not, you need not bring fruitless weakSelves into the world.

Apple even forces these warnings upon us with the API to their UIPageViewController, which includes a set method (which triggers these warnings–as mentioned elsewhere–thinking you are setting a value to an ivar that is a block) and a completion handler block (in which you'll undoubtedly refer to yourself).

Here's some compiler directives to remove the warning from that one line of code:

#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

Solution 6 - Objective C

Adding two cents on improving precision and style. In most cases you will only use one or a couple of members of self in this block, most likely just to update a slider. Casting self is overkill. Instead, it's better to be explicit and cast only the objects that you truly need inside the block. For example, if it's an instance of UISlider*, say, _timeSlider, just do the following before the block declaration:

UISlider* __weak slider = _timeSlider;

Then just use slider inside the block. Technically this is more precise as it narrows down the potential retain cycle to only the object that you need, not all the objects inside self.

Full example:

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

Additionally, most likely the object being cast to a weak pointer is already a weak pointer inside self as well minimizing or eliminating completely the likelihood of a retain cycle. In the example above, _timeSlider is actually a property stored as a weak reference, e.g:

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

In terms of coding style, as with C and C++, variable declarations are better read from right to left. Declaring SomeType* __weak variable in this order reads more naturally from right to left as: variable is a weak pointer to SomeType.

Solution 7 - Objective C

I ran into this warning recently and wanted to understand it a bit better. After a bit of trial and error, I discovered that it originates from having a method start with either "add" or "save". Objective C treats method names starting with "new", "alloc", etc as returning a retained object but doesn't mention (that I can find) anything about "add" or "save". However, if I use a method name in this way:

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

I will see the warning at the [self done] line. However, this will not:

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

I will go ahead and use the "__weak __typeof(self) weakSelf = self" way to reference my object but really don't like having to do so since it will confuse a future me and/or other dev. Of course, I could also not use "add" (or "save") but that's worse since it takes away the meaning of the method.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser1845209View Question on Stackoverflow
Solution 1 - Objective CTimView Answer on Stackoverflow
Solution 2 - Objective CiiFreemanView Answer on Stackoverflow
Solution 3 - Objective CWarif Akhand RishiView Answer on Stackoverflow
Solution 4 - Objective CChris SuterView Answer on Stackoverflow
Solution 5 - Objective CbshirleyView Answer on Stackoverflow
Solution 6 - Objective CLuis ArtolaView Answer on Stackoverflow
Solution 7 - Objective CRay M.View Answer on Stackoverflow