3

I have a block retain cycle question,
suppose I have the following 3 methods, all in one class.

- (void)foo1WithBlock:(void (^)(BOOL success))completion
   // do something...
   completion(YES)
}  

- (void)foo2 {
   // do something...
}  

- (void)foo3 {
   [self foo1WithBlock:^(BOOL success) {
       [self foo2];
   }]; 
}

Will foo3 create a retain cycle?

Mario
  • 2,431
  • 6
  • 27
  • 34

3 Answers3

9

No, there is no retain cycle.

However, self will be captured. This means, self will be imported into the lexical scope of the compound statement (the statements executed by the block). This involves making a copy of the "outside" variable self which creates the block's variable self.

A block can be copied and released. A block_copy operation will "move" the captured variables onto the heap. They exists there until the block gets destroyed via block_release operation. The compiler/runtime provides internal functions to copy and release a block and performs them when required. For example, if the block is executed asynchronously via dispatch_async() the block will have to be copied first, and later when the block is finished, released again. These block_copy and block_release operations are inserted by the compiler and executed by the runtime, so don't worry.

If the block will be copied, as an effect self will be retained, and released again when the block gets released - which happens when the block has been finished.

In effect, this guarantees that self within the block and during the life-time of the block is valid (that is, it won't get deallocated), whether it is called synchronously or asynchronously. When the block has been executed asynchronously, the block has been copied, and thus self has been retained. And self will be only released again until after the block finishes. This also means, that the block "will" extend the life time of self until after the block finishes.

CouchDeveloper
  • 18,174
  • 3
  • 45
  • 67
  • Nice explanation. In Mario's question, is there any real benefit in using a "__weak" pointer to "self" before calling "foo1withBlock"? For example, "self" could be view controller that initiates a network request. If you didn't want to wait for it to complete, you might want to navigate away, have it deallocate and leave the block with a nil reference to self? – rmigneco Jul 09 '14 at 03:44
  • 1
    @SpinalTapFan11 There are indeed scenarios where you want to use a "weak self" vs a "strongly captured self". The decision whether you use a weak or a strongly captured pointer is not a "benefit", rather it depends on your use case and what you want to accomplish: see here: http://stackoverflow.com/questions/21987067/using-weak-self-in-dispatch-async-function/21988407#21988407 – CouchDeveloper Jul 09 '14 at 05:26
0

No, there will no retain cycle..as you are not calling each other method here.

Samkit Jain
  • 2,523
  • 16
  • 33
  • I'm not sure what you mean by "you are not calling each other method here". If the code *was* doing that, you'd get more than a retain cycle... – trojanfoe Feb 19 '14 at 13:19
-1
- (void)foo3 {
   [self foo1WithBlock:^(BOOL success) {
       [self foo2];
   }]; 
}

In this case, if you don't understand the lifetime of the block you are passing to foo1WithBlock: it is probably a good idea to use this idiom to prevent a the block from inappropriately extending the lifetime of self.

- (void)foo3 {
   __weak ParentType *wself = self;  //create a weak reference (weak automatically gets set to nil on dealloc)
   [self foo1WithBlock:^(BOOL success) {
       ParentType *self = wself;  //create a local strong reference for the life of the block.
       [self foo2];
   }]; 
}

if you are using cocoapods libextobjc has a EXTScope which provides helper macros for this:

- (void)foo3 {
   @weakify(self);
   [self foo1WithBlock:^(BOOL success) {
       @strongify(self);
       [self foo2];
   }]; 
}
Jon
  • 462
  • 4
  • 13