Quick Tip - Blocks, Self and Retain Cycle

        This is a quick tip for all those programers out there who are still confused and are not convinced about the malicious stuffs that can happen if blocks are not handled well. Most of us by now might have figured out the power of blocks and would have realised its significance which is not just "deferred calling". If blocks would have just served the purpose of deferred calling, then it would have just turned out to be an option in the rest with the likes of delegations and @selectors already existing in Objective C. Isnt it ?

        So I would quickly like to explain what else a block can do and more would I here emphasize on how to not screw up your code using blocks. Before jumping into the problem and an associated tip lets quickly see what this "Retain Cycle" is all about.

Retain Cycles
  
        Memory is the most crucial factor for the best performance to be achieved on any mobile device. As a responsible programmer one should always have the most possible concern to clean up memory much before it burdens the user with its sluggishness. All thanks to Apple that they have come up with ARC to the developers ease that he need not explicitly trace the retain count of an object to signal a memory release on it !! But that has further other implications as well which you can not ignore. Nothing that comes for free is totally free infact :P You still need to be a responsible programer to make sure that you dont end up having "Retain Cycles".  

      While we know retain count is a quantitative measure for the number of references an object has, we also should know that there is an equally significant qualitative measure for the same which are "Strong" and "Weak" references. So how does the quality give a hit to your quantity ? Its simple. Lets see something quickly with a code snippet as well.

     A weak reference(qualitative) to an object which has retain count(quantitative) of more than zero still cant ensure that the object will not be released !! So when you have weak reference to an object, all it means is that you want to refer to the object only till the time its available in the memory. Here all it means is that the weak reference objects would be cleaned up by ARC at times of memory constraints. On the other hand when you hold a strong reference to an object, the object is guaranteed to stay in memory till the object which is holding a reference to it doesnt free up.

/****** Example for Weak References ********/

   @implementation ABC
{
      __weak NSString  *msg = @"Hello Programers!!"

     if(  msg != nil ) // You are never sure whether this evaluates to true or false. 
      {
          // Some code here
      }
}

 In the above code snippet as msg object is referenced weakly by the class, the object might be released at any point of time much before the class scope ends.

/****** Example for Strong References ********/

   @implementation ABC
{
      NSString  *msg = @"Hello Programers!!"

     if(  msg != nil ) // You are sure this evaluates to true. 
      {
          // Some code here
      }
}

In the above code the class holds a strong reference to the msg object. So the msg object is guaranteed to persist in memory till the scope of the class ends.

    So after having known all these details about references lets see, how references if not handled well could create retain cycles.

 "Two objects both holding strong references to each other thereby both being held in memory forever is called retain cycle"

   Lets quickly see a code snippet for the same.

@class ClassB // Forward declaration of class B here
@interface ClassA
@property (nonatomic,strong) Class B *obj; // A has an object of strong reference of class B
@end


@interface ClassB
@property (nonatomic,strong) Class A *obj; // B has an object of strong reference of class A
@end

    Its clearly evident that both of the objects hold a strong reference to each other thereby resulting in a "Retain Cycle". Its of atmost priority to a programer that max care should be taken to avoid retain cycles. Now you might understand why protocols should always be of weak references ? (Another most frequently asked interview question). The reason being to avoid Retain Cycles.  


Blocks, Self And Retain Cycles

  Now lets quickly come back to for we saw a big prologue :P  Its always easy to figure out Retain Cycles happening in a plain code than in a block. "A class always holds a strong reference to a block.  We know blocks are treated to be objects of sequential statements in Objective C. Thereby as any other objects would have, blocks would also have references associated with them. Whenever a block is being called, by default self (the class object) holds a strong reference to the block. Lets see a snippet of code quickly for the same."

// Blocks Classes and Strong References

@implementation SomeClassA
{
      // Block call happens here. SomeClassbObj is an object of some another class B 
     // someid is some random id for which we need to fetch results
   
      // Block call 
      [SomeClassBObj getDetailsForId : someid
                                 success : ^(NSArray *result)
                                                 {}];     
}
@end

   From the above explanation and code its quite easily understandable that self(the class object) has strong reference to the block. Now, using self in the same block would create a Retain Cycle isnt it ? So we should thereby make sure we dont use self itself directly in a block. We should make sure we use a weak reference of it in the block. Lets see both the good and bad practice . 

/************ Code that results in Retain Cycle ****************/

@implementation SomeClassA

{

      // Block call happens here. SomeClassbObj is an object of some another class B 

     // someid is some random id for which we need to fetch results


     // Block call 

      [SomeClassBObj getDetailsForId : someid
                                 success : ^(NSArray *result)
                                                 {
                                                    success ( [self doSomething] ) // self being accessed in block directly. Retin Cycle results here.
                                                 }];
}

-(NSArray*)doSomething
{
        // Some code
}
@end


/************ Code that doesnt result in Retain Cycle ****************/

@implementation SomeClassA

{

      // Block call happens here. SomeClassbObj is an object of some another class B 

     // someid is some random id for which we need to fetch results
    

      // Block call 

      __weak SomeClassA *ref = self;
      [SomeClassBObj getDetailsForId : someid
                                 success : ^(NSArray *result)
                                                 {
                                                          if( ref == nil )
                                                              success (nil)
                                                          else
                                                              success ( [ref doSomething] ) // use weak reference here
                                                 }];
}

-(NSArray*)doSomething
{
        // Some code
}
@end

     So here ends my quick tip !! I wish it helps you all understand Retain Cycles better and take care of the same while using blocks or protocols. Please do write in your feedback. 







4 comments:

  1. Great topic and nice explanation!
    I'll use your suggestion to immediately flatten occupation memory.
    Blocks are awesome but even insidious!

    Thank you,
    F.

    ReplyDelete
  2. shouldn't that second block be:

    @implementation SomeClassA

    {

    // Block call happens here. SomeClassbObj is an object of some another class B
    // someid is some random id for which we need to fetch results

    // Block call
    __weak SomeClassA *ref = self;
    [SomeClassBObj getDetailsForId : someid
    success : ^(NSArray *result)
    {
    if( ref == nil )
    success (nil)
    else
    success ( [ref doSomething] ) // use the weak reference
    }];
    }

    ReplyDelete
    Replies
    1. Hey Thanks a lot for pointing out the mistake.. Yeah u r right.. Changing it right away.. It should be a weak reference there.. My bad :(

      Delete