Avoid strong reference cycles

Share the joy
  •  
  •  
  •  
  •  

With the introduction of ARC, memory management became easier. However, even though you don’t have to worry about when to retain and release, there are still a few rules you need to know in order to avoid memory problems. In this post we’re going to talk about strong reference cycles.

What exactly is a strong reference cycle? Let’s assume that you have two objects, object A and object B. If object A holds a strong reference to object B and object B has a strong reference to object A then we have a strong reference cycle. We’re going to talk about two very common situations where you need to be careful about reference cycles: blocks and delegates.

1. Delegates

Delegation is a commonly used pattern in Objective C. In this case one object acts on behalf of or in coordination with another object. The delegating object keeps a reference to the other object (the delegate) and at the appropriate time it sends a message to it. The delegate is able to respond by updating the appearance or the state of the application.

One example from the API is the UITableView and its delegate. In this example the table view has a reference to its delegate and the delegate has a reference back to the table view. This means that each one is keeping the other alive so even if there are no other objects pointing to the delegate or the table view, the memory doesn’t get deallocated.

Let’s consider a custom example:

#import <Foundation/Foundation.h>

@class ClassA;

@protocol ClassADelegate <NSObject>

-(void)classA:(ClassA *)classAObject didSomething:(NSString *)something;

@end

@interface ClassA : NSObject

@property (nonatomic, strong) id<ClassADelegate> delegate;

@end

This will generate a retain cycle in an ARC world. To prevent this all we need to do is change the reference to our delegate to be weak:

@property (nonatomic, weak) id<ClassADelegate> delegate;

A weak reference does not imply ownership or responsibility between two objects and does not keep an object alive. If there are no other objects pointing to the delegate and the delegating object, then first the delegate will get deallocated and so it will release its strong reference to the delegating object. With nobody pointing to it the delegating object will also get released.

2. Blocks

Blocks are chunks of code, similar to C functions, but in addition to executable code they may contain variable bindings to stack or heap memory. A block can therefore maintain a set of data that it can use to impact behaviour when executed. Because blocks maintain the data needed for execution of the code, they are particularly useful as callbacks.

Blocks are Objective C objects, however there are some memory management rules that only applies to blocks, no other Objective C objects.

Blocks maintain strong references to any captured objects, including self, so it’s very easy to end up with a strong reference cycle. If a class has a property for a block like this:

@property (copy) void (^block)(void);

And in its implementation you have a method like this:

- (void)methodA {

    self.block = ^{

        [self methodB];
    };
}

then you’ve got yourself a strong reference cycle: the object (self) has a strong reference to the block and the block just captured a strong reference to self.

Note: For block properties its a good practice to use copy, because a block needs to be copied to keep track of its captured state outside of the original scope.

In order to avoid this strong reference cycle we need to use weak references again. This is how the code would look like:

- (void)methodA {

    ClassB * __weak weakSelf = self;

    self.block = ^{

        [weakSelf methodB];
    };
}

By capturing the weak reference to self, the block won’t maintain a strong relationship to the object. If the object is deallocated before the block is called the weakSelf pointer will simply be set to nil. While this is great because there won’t be a memory problem, if the pointer is nil then our method inside the block won’t get called and so the block won’t  have the expected behaviour. To avoid this, we’re going to alter our example a bit further:

- (void)methodA {

    __weak ClassB *weakSelf = self;

    self.block = ^{

        __strong ClassB *strongSelf = weakSelf;

        if (strongSelf) {

            [strongSelf methodB];
        }
    };
}

We are creating a strong self reference inside the block. This reference will belong to the block and it will be alive for as long as the block is. It won’t prevent the self object for being deallocated so we are still avoiding the strong reference cycle.

Not all strong reference cycles are as easy to see as the one in my example so you might consider using a weak reference whenever your block code gets a bit more complicated.

These are two common patterns where strong reference cycles can appear.  As seen, it’s very easy to break these cycles with weak reference as long as you can correctly identify them. You need to be mindful of memory management even if ARC made is easier for all of us.

 


Share the joy
  •  
  •  
  •  
  •  

2 thoughts on “Avoid strong reference cycles

  1. Most of the time, developers tend to ignore the importance of strong and weak reference type to objects which if not fixed will lead to unbounded memory growth which forces iOS to kill the app. Delegates and blocks are the most common places where one tends to ignore these. This article is a good value addition.

Leave a Reply

Your email address will not be published. Required fields are marked *

*