Always pass weak reference of self into block in ARC?

119,677

Solution 1

It helps not to focus on the strong or weak part of the discussion. Instead focus on the cycle part.

A retain cycle is a loop that happens when Object A retains Object B, and Object B retains Object A. In that situation, if either object is released:

  • Object A won't be deallocated because Object B holds a reference to it.
  • But Object B won't ever be deallocated as long as Object A has a reference to it.
  • But Object A will never be deallocated because Object B holds a reference to it.
  • ad infinitum

Thus, those two objects will just hang around in memory for the life of the program even though they should, if everything were working properly, be deallocated.

So, what we're worried about is retain cycles, and there's nothing about blocks in and of themselves that create these cycles. This isn't a problem, for example:

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

The block retains self, but self doesn't retain the block. If one or the other is released, no cycle is created and everything gets deallocated as it should.

Where you get into trouble is something like:

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

Now, your object (self) has an explicit strong reference to the block. And the block has an implicit strong reference to self. That's a cycle, and now neither object will be deallocated properly.

Because, in a situation like this, self by definition already has a strong reference to the block, it's usually easiest to resolve by making an explicitly weak reference to self for the block to use:

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

But this should not be the default pattern you follow when dealing with blocks that call self! This should only be used to break what would otherwise be a retain cycle between self and the block. If you were to adopt this pattern everywhere, you'd run the risk of passing a block to something that got executed after self was deallocated.

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

Solution 2

I totally agree with @jemmons:

But this should not be the default pattern you follow when dealing with blocks that call self! This should only be used to break what would otherwise be a retain cycle between self and the block. If you were to adopt this pattern everywhere, you'd run the risk of passing a block to something that got executed after self was deallocated.

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

To overcome this problem one can define a strong reference over the weakSelf inside the block:

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

Solution 3

You don't have to always use a weak reference. If your block is not retained, but executed and then discarded, you can capture self strongly, as it will not create a retain cycle. In some cases, you even want the block to hold the self until the completion of the block so it does not deallocate prematurely. If, however, you capture the block strongly, and inside capture self, it will create a retain cycle.

Solution 4

As Leo points out, the code you added to your question would not suggest a strong reference cycle (a.k.a., retain cycle). One operation-related issue that could cause a strong reference cycle would be if the operation is not getting released. While your code snippet suggests that you have not defined your operation to be concurrent, but if you have, it wouldn't be released if you never posted isFinished, or if you had circular dependencies, or something like that. And if the operation isn't released, the view controller wouldn't be released either. I would suggest adding a breakpoint or NSLog in your operation's dealloc method and confirm that's getting called.

You said:

I understand the notion of retain cycles, but I am not quite sure what happens in blocks, so that confuses me a little bit

The retain cycle (strong reference cycle) issues that occur with blocks are just like the retain cycle issues you're familiar with. A block will maintain strong references to any objects that appear within the block, and it will not release those strong references until the block itself is released. Thus, if block references self, or even just references an instance variable of self, that will maintain strong reference to self, that is not resolved until the block is released (or in this case, until the NSOperation subclass is released.

For more information, see the Avoid Strong Reference Cycles when Capturing self section of the Programming with Objective-C: Working with Blocks document.

If your view controller is still not getting released, you simply have to identify where the unresolved strong reference resides (assuming you confirmed the NSOperation is getting deallocated). A common example is the use of a repeating NSTimer. Or some custom delegate or other object that is erroneously maintaining a strong reference. You can often use Instruments to track down where objects are getting their strong references, e.g.:

record reference counts in Xcode 6

Or in Xcode 5:

record reference counts in Xcode 5

Share:
119,677

Related videos on Youtube

the_critic
Author by

the_critic

Updated on October 11, 2020

Comments

  • the_critic
    the_critic over 3 years

    I am a little confused about block usage in Objective-C. I currently use ARC and I have quite a lot of blocks in my app, currently always referring to self instead of its weak reference. May that be the cause of these blocks retaining self and keeping it from being dealloced ? The question is, should I always use a weak reference of self in a block ?

    -(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);
        }
    }
    
  • the_critic
    the_critic over 10 years
    Well I just execute the block as a callback and I would not want self to be dealloced at all. But it seems as if I were creating retain cycles because the View Controller in question does not get dealloced...
  • Léo Natan
    Léo Natan over 10 years
    For example, if you have a bar button item which is retained by the view controller, and you capture self strongly in that block, there will be a retain cycle.
  • Rob
    Rob over 10 years
    @MartinE. You should just update your question with code samples. Leo is quite right (+1), that it doesn't necessarily cause strong reference cycle, but it may, depending upon how you use these blocks. It will be easier for us to help you if you provide code snippets.
  • the_critic
    the_critic over 10 years
    @LeoNatan I understand the notion of retain cycles, but I am not quite sure what happens in blocks, so that confuses me a little bit
  • Léo Natan
    Léo Natan over 10 years
    In the above code, your self instance will be released once the operation finishes and the block is released. You should read up on how blocks work and what and when they capture in their scope.
  • the_critic
    the_critic over 10 years
    So when is a block actually retained, if I may ask ?
  • Léo Natan
    Léo Natan over 10 years
    @MartinE. For example, if you have a property (or strong instance variable) to the block. Also, I already mentioned if you have a bar button item with block implementation (from an open source implementation, for instance).
  • Léo Natan
    Léo Natan over 10 years
    Another example would be if the operation is retained in the block creator and not released once it is finished. +1 on the nice write up!
  • Léo Natan
    Léo Natan over 10 years
    Another example would be if the operation is retained in the block creator and not released once it is finished.
  • Rob
    Rob over 10 years
    @LeoNatan Agreed, though the code snippet represents it as a local variable which would be released if he's using ARC. But you're quite right!
  • Léo Natan
    Léo Natan over 10 years
    Yes, I was just giving an example, as the OP requested in the other answer.
  • Danyun Liu
    Danyun Liu over 10 years
    I am not sure A retain B, B retain A will do a infinite cycle. From the perspective of reference count, A and B's reference count is 1. What cause a retain cycle for this situation is when there is no other group have strong reference of A and B outside -- it means we cannot reach these two object(we cannot control A to release B and vice versa), therefor, A and B reference each other to keep themselves both alive.
  • jemmons
    jemmons over 10 years
    @Danyun While it's true that a retain cycle between A and B is not unrecoverable until all other references to these objects have been released, that doesn't make it any less a cycle. Conversely, just because a particular cycle might be recoverable doesn't mean it's ok to have it in your code. Retain cycles are a smell of bad design.
  • Danyun Liu
    Danyun Liu over 10 years
    @jemmons Yes, we should always avoid a retain cycle design as much as we can.
  • Ruslan Mansurov
    Ruslan Mansurov over 9 years
    What if ViewController has strong reference to object paginator and we executing code [paginator setCompletionBlockWithSuccess:^{ [self ...]; } failure:^{ [self ...]; }]; Should we use weak references when using self?
  • jemmons
    jemmons over 9 years
    @Master It's impossible for me to say. It depends completely on the implementation of your -setCompleteionBlockWithSuccess:failure: method. But if paginator is owned by ViewController, and these blocks don't get called after ViewController would be released, using a __weak reference would be the safe move (because self owns the thing that owns the blocks, and so is likely to still be around when the blocks call it even though they don't retain it). But that's a lot of "if"s. It really depends on what this is supposed to do.
  • Just a coder
    Just a coder almost 9 years
    In your suspicious example, How is it possible that weakself can ever be nil? although it is not retained, the weak self object is declared right outside the block right? Wont it still be able to pick up its value?
  • jemmons
    jemmons almost 9 years
    @Jai No, and this is at the heart of the memory management problem with blocks/closures. Objects get deallocated when nothing owns them. MyObject and SomeOtherObject both own the block. But because the block's reference back to MyObject is weak, the block does not own MyObject. So while the block is guaranteed to exist as long as either MyObject or SomeOtherObject exist, there's no guarantee that MyObject will exist as long as the block does. MyObject can be completely deallocated and, as long as SomeOtherObject still exists, the block will still be there.
  • mskw
    mskw almost 9 years
    Wouldn't strongSelf increment the reference count for weakSelf? Thus creating a retain cycle?
  • Pierre Houston
    Pierre Houston over 8 years
    Retain cycles only matter if they exist in the static state of the object. While code is executing and its state is in flux, multiple and possibly redundant retains are fine. Anyway regarding this pattern, capturing a strong reference there doesn't do anything for the case of self getting deallocated before the block runs, that can still happen. It does ensure self doesn't get deallocated while executing the block. This matters if the block does async operations itself giving a window for that to happen.
  • Huibin Zhang
    Huibin Zhang over 8 years
    @smallduck, your explanation is great. Now I understand this better. Books not covered this, thanks.
  • malhal
    malhal over 8 years
    It would be good to add a code snippet to show how to solve the suspicious example, which is in Ilker Baltaci's answer.
  • jemmons
    jemmons over 8 years
    @malhal The answer is not to create a strong reference to weakSelf. You should just to use self.
  • ArdenDev
    ArdenDev about 8 years
    @jemmons, Nice explanation. Just to clarify, if we create multiple methods in a class which has its own block based API i.e. using success/failure or completion handler and we have these multiple methods to avoid having one huge nested block .... using self to just call those other methods in the same class shouldn't create a retain cycle. correct ? However if any of those methods reference a strong property of the same class, that creates a retain cycle ?
  • ArdenDev
    ArdenDev about 8 years
    should strongSelf approach be used to ensure the nested code does get executed ?
  • Rob
    Rob over 7 years
    By the way, Xcode 8 has "Debug Memory Graph", which is an even easier ways to find strong references to objects that have not been released. See stackoverflow.com/questions/30992338/….
  • Mecki
    Mecki almost 7 years
    Blocks can live on stack or on heap. When created, they always exist on stack and are only copied to heap by explicit copy ([... copy] or Block_copy) or implicit copy (copy property w/o ARC or assigning it to any strong non-stack variable w/ ARC, including ivar and properties). Only when moved to heap, a block must retain all objects it captured, on stack it doesn't have to, just like Obj-C won't retain self when calling a method (self can die while a method of it is executed!). Thus I doubt that enumerateObjectsUsingBlock retainsself as that block is never copied to heap.
  • Ethan
    Ethan about 6 years
    This isn't a good example of strongSelf, because the explicit addition of strongSelf is exactly what the runtime would do anyway: on the doSomething line, a strong reference is taken for the duration of the method call. If weakSelf was already invalidated, the strong ref is nil and the method call is a no-op. Where strongSelf helps is if you have a series of operations, or accessing a member field (->), where you want to guarantee you actually got a valid reference and hold it continuously across the whole set of operations, e.g. if ( strongSelf ) { /* several operations */ }
  • Ethan
    Ethan about 6 years
    For reference, this is the difference between escaping and non escaping blocks in Swift. Non-escaping blocks aren't stored, and thus you can use strong references. Escaping blocks could cause a cycle. Objective-C appears to also track block lifecycle to put up a warning about needing a weak reference, but currently does not offer the keywords to mark blocks non-escaping in the interface, so it has to be a little paranoid about supplying the warning when it can't tell where the block is going.