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

89,948

Solution 1

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

__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

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


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

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

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
Share:
89,948

Related videos on Youtube

user1845209
Author by

user1845209

Updated on January 25, 2020

Comments

  • user1845209
    user1845209 over 4 years

    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
    }];
    
    • Tim
      Tim over 11 years
      Is timerDisp a property on the class?
    • user1845209
      user1845209 over 11 years
      Yes, @property(nonatomic,strong)UILabel *timerDisp;
    • Carl Veazey
      Carl Veazey over 11 years
      What's this: player(AVPlayer object) and timerDisp(UILabel)?
    • user1845209
      user1845209 about 11 years
      AVPlayer *player; UILabel *timerDisp;
    • Glenn Maynard
      Glenn Maynard about 10 years
      The real question is how to silence this warning without an unnecessary weak reference on self, when you know the circular reference will be broken (eg. if you always clear the reference when a network request finishes).
  • user1845209
    user1845209 over 11 years
    __weak typeof(self) weakSelf = self;...is giving error "the current deployment target does not support automated __weak references"...my deployment target is 4.3
  • Tim
    Tim over 11 years
    Try using __unsafe_unretained instead.
  • user1845209
    user1845209 over 11 years
    Resolved. use this instead: __unsafe_unretained typeof(self) weakSelf = self; thanks fo the help @Tim
  • Eric J.
    Eric J. about 11 years
    This would be a stronger answer if you also explain it.
  • Chris Suter
    Chris Suter about 11 years
    Good answer, but I take small issue with you saying: “you can't refer to self or properties on self from within a block that will be strongly retained by self.” This is not strictly true. Please see my answer below. Better to say, “you must take great care if you refer to self…”
  • erikprice
    erikprice about 11 years
    I don't see a retain cycle in the OP's code. The block is not strongly retained by self, it's retained by the main dispatch queue. Am I wrong?
  • bobobobo
    bobobobo about 11 years
    Is this necessary, even with ARC?
  • Tim
    Tim about 11 years
    @erikprice: you're not wrong. I interpreted the question to be primarily about the error Xcode presents ("How can I avoid this warning in xcode"), rather than about the actual presence of a retain cycle. You're correct in saying no retain cycle is evident just from the snippet OP provided.
  • Tim
    Tim about 11 years
    @bobobobo: yes, this is necessary in ARC.
  • erikprice
    erikprice about 11 years
    I see, thanks. The net effect is similar, as long as the main dispatch queue retains the block then self cannot be deallocated. In the case of -addPeriodicTimeObserverWithTimeInterval:, it's possible for a block that retains self to be enqueued before the previous block that retains self is executed, creating not so much a retain cycle, but a permanent state of self being retained.
  • Bagusflyer
    Bagusflyer about 11 years
    If I use __weak typeof(self) weakSelf = self; then all the properties for this object will be NULL.
  • iCoder86
    iCoder86 almost 11 years
  • abbood
    abbood over 10 years
  • Matt
    Matt almost 10 years
    What if you did typeof(self) strongSelf = self; outside of the block (instead of __weak) then in the block said strongSelf = nil; after usage? I don't see how your example ensures that weakSelf isn't nil by the time the block executes.
  • Matt
    Matt almost 10 years
    What if you did typeof(self) strongSelf = self; outside of the block (instead of __weak) then in the block said strongSelf = nil; after usage?
  • Tim
    Tim almost 10 years
    In my sample project, it does look like that will avoid this warning. Keep in mind, though, that you are still temporarily establishing a retain cycle - since your strongSelf is (as you say) strong, self and the block will retain each other. Setting strongSelf to nil should break the cycle, though. Whether this approach applies to your situation is left to individual discretion.
  • Warif Akhand Rishi
    Warif Akhand Rishi almost 10 years
    To avoid possible retain cycles, we establish a weak self reference outside any block that uses self in its code. In ur way, u have to ensure that the block is executed. Another block of ur code is now responsible for freeing ur previously retained memory.
  • Warif Akhand Rishi
    Warif Akhand Rishi almost 10 years
    @Matt the purpose of this example is not to make the weakSelf retained. The purpose is, if the weakSelf is not nil, make a strong reference inside the block. So once the block starts executing with self, self doesn't become nil inside the block.
  • Luis Artola
    Luis Artola over 8 years
    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, better be explicit and cast only the objects that you truly need in 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 you need, not all the objects inside self
  • Itai Spector
    Itai Spector about 8 years
    You can use this as well: __weak __typeof(&*self)weakSelf = self;
  • auspicious99
    auspicious99 over 2 years
    When I only had the weakself, Xcode was complaining about my use of it, as it could be nil. This answer solved the problem. Deserves more upvotes.