Retain cycles in ARC are kind of like a Japanese B-horror movie. When you start as a Cocoa/Cocoa Touch developer, you don’t even bother about their existence. Then one day one of your apps starts crashing because of memory leaks and suddenly you become aware of them, and start seeing retain cycles like ghosts everywhere. As the years pass, you learn to live with them, detect them and avoid them… but the final scare is still there, looking for its chance to creep in.

One of the biggest disappointments of swift for many developers (including me) was that Apple kept ARC for memory management. ARC unfortunately doesn’t include a circular reference detector, so it’s prone to retain cycles, thus forcing the developers to take special precautions when writing code.

Retain cycles are a obscure topic for iOS developers. There are lot of misinformation in the web [1][2], to the point of people giving wrong suggestions and “fixes” that could even potentially lead to problems and crashes in your App. In this article, I would like to shed some light on the subject matter.

Some (brief) theory

Memory management in Cocoa dates back to Manual Retain Release (MRR). In MRR, the developer would declare that an object must be kept in memory by claiming ownership on every object created, and relinquishing it when the object was not needed anymore. MRR implemented this ownership scheme by a reference counting system, in which each object would be assigned a counter indicating how many times it has been “owned”, increasing the counter by one, and decreasing it by one each time it was released. The object would then cease to exist when its reference counter reached zero. Having to take care of reference counting manually was really an annoyance for the developer, so Apple developed ARC (Automated Reference Counting) to free the developers of having to manually add the retain and release instructions, allowing them to focus in the problem being solved by the App. Under ARC, a developer will define a variable as “strong” or “weak”. A weak variable will not be retained by the object declaring it, whereas a strong declaration will retain the object and increment its reference count.

retainCycle

Why should I care?

The problem with ARC is that it’s prone to retain cycles. A retain cycle occurs when two different objects contain a strong reference to each other. Think of a Book object that contains a collection of Page objects, and that every Page object has a property pointing to the book the page is contained in. When you release the variables that point to Book and Page, they still have strong references among them, so they won’t be released and their memory freed, even though there are no variables pointing to them.

Unfortunately, not all retain cycles are so easy to spot. Transitive relationships between objects (A references B, which in turn references C, which happens to reference A) can lead to retain cycles. To make things worse, both Objective-C blocks and Swift closures are considered independent memory objects, so any reference to an object inside a block or closure will retain its variable, thus leading to a potential retain cycle if the object also retains the block.

Retain cycles can be potentially dangerous for an App, leading to high memory consumption, bad performance and crashes. However, there is no clear documentation from Apple about the different scenarios in which a retain cycle may occur, and how to avoid them. This has lead to a number of misconceptions and bad programming practices.

Use case scenarios

So, without further ado, let’s analyze some scenarios to determine whether or not they cause a retain cycle and how to avoid them:

Parent-child object relationship

This is the classic example of retain cycle, and unfortunately, the only one addressed by the Apple documentation. It’s the example of the Book and the Page objects I described before. The classic solution for this situation is defining the variable representing the parent in the Child class as weak, thus avoiding the retain cycle

class Parent {
   var name: String
   var child: Child?
   init(name: String) {
      self.name = name
   }
}
class Child {
   var name: String
   weak var parent: Parent!
   init(name: String, parent: Parent) {
      self.name = name
      self.parent = parent
   }
}

The fact that parent is a weak variable in Child forces us to define it as an optional type in swift. An alternative for not having to use an optional is declaring parent as “unowned” (meaning that we don’t claim ownership or memory management on the variable). However, in this case we must be extremely careful to ensure that Parent doesn’t become nil as long as there is a Child instance pointing to it, or we will end up with a nasty crash:

class Parent {
   var name: String
   var child: Child?
   init(name: String) {
      self.name = name
   }
}
class Child {
   var name: String
   unowned var parent: Parent
   init(name: String, parent: Parent) {
      self.name = name
      self.parent = parent
   }
}
var parent: Parent! = Parent(name: "John")
var child: Child! = Child(name: "Alan", parent: parent)
parent = nil
child.parent // <== possible crash here!

In general, the accepted practice is that the parent must own (strongly reference) its children objects, and they should only keep a weak reference to their parent. The same applies for collections, they must own the objects they contain.

Blocks and closures contained in instance variables

Another classic example, though not so intuitive. As we explained before, closures and blocks are independent memory objects, and retain the objects they reference, so if we have class with a closure variable, and that variable happens to reference a property or method in the owning object, there would be a retain cycle because the closure is “capturing” self by creating a strong reference to it.

class MyClass {
   lazy var myClosureVar = {
      self.doSomething()
   }
}

The solution in this case implies defining a “weak” version of self and sending that weak reference to the closure or block. In Objective-C we will define a new variable:

- (id) init() {
   __weak MyClass * weakSelf = self;
   self.myClosureVar = ^{
      [weakSelf doSomething];
   }
}

Whereas in Swift we just need to specify the “[weak self] in” as the closure starting parameter:

var myClosureVar = {
   [weak self] in
   self?.doSomething()
}

This way, when the closures reaches the end, the self variable is not strongly retained, so it gets released, breaking the cycle. Note how self becomes an optional inside the closure when declared weak.

GCD: dispatch_async

Contrary to what’s usually believed, dispatch_async per se will NOT cause a retain cycle

dispatch_async(queue, { () -> Void in
   self.doSomething(); 
});

Here, the closure has a strong reference to self, but the instance of the class (self) does not have any strong reference to the closure, so as soon as the closure ends, it will be released, and so no cycle will be created. However, sometimes it’s (incorrectly) assumed that this situation will lead to a retain cycle. Some developers even get to the point of recommending all references to “self” inside a block or closure to be weak:

dispatch_async(queue, {
   [weak self] in
   self?.doSomething()
})

This is not a good practice for every single case in my opinion. Let’s suppose that we have an object that launches a long background task (like downloading some content from the network), and then invokes a method of “self”. If we pass a weak reference to self, our class could end its lifecycle and get released before the closure ends, so when it invokes the doSomething() method, our class instance doesn’t exist anymore, so the method never gets executed. The proposed solution (by Apple) for this situation is declaring a strong reference to the weak reference (???) inside the closure:

dispatch_async(queue, {
   [weak self] in
   if let strongSelf = self {
      strongSelf.doSomething()
   }
})

Not only do I find this syntax disgusting, unintuitive and tedious, but I think it defeats the entire purpose of having closures as independent processing entities. I think it’s much better to understand the lifecycle of our objects and know exactly when should we declare a weak inner version of our instance and which are the implications for the lifetime of our objects. But then again, this is something that distracts me from the problem my App is solving, it’s code that wouldn’t need to be written if Cocoa didn’t use ARC.

Local closures and blocks

Closures and blocks local to a function, not referenced or contained in any instance or class variable will NOT cause a retain cycle per se. One of the common examples of this is the animateWithDuration method of UIView:

func myMethod() {
   ...
   UIView.animateWithDuration(0.5, animations: { () -> Void in
      self.someOutlet.alpha = 1.0
      self.someMethod()
   })
}

As with dispatch_async and other GCD related methods, we should not worry about retain cycles in local closures and blocks not referenced strongly by the class instance.

Delegation scheme

The delegation scheme is one of the typical scenarios where you want to use a weak reference to avoid a retain cycle. It is always a good practice (and generally safe) to declare the delegate as weak. In Objective-C:

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

And in swift:

weak var delegate: MyCustomDelegate?

In most cases, the delegate of an object has instantiated the object or is supposed to outlast the object (and react to the delegate methods), so under a good class design we should not find any problems regarding the lifetime of objects.

Debugging retain cycles with Instruments.

It doesn’t matter how hard I try to avoid retain cycles, sooner or later I forget to include a weak reference and accidentally create one (thanks for nothing, ARC!). Luckily, the Instruments application included in the XCode suite is a great tool for detecting and locating retain cycles. It is always a good practice to profile your App once the development phase is over and before submitting it to the Apple Store. Instruments has many templates for profiling different aspects of the App, but the one we’re interested in is the “Leaks” option.

instruments

Once Instruments opens, you should start your application and do some interactions, specially in the areas or view controllers you want to test. Any detected leak will appear as a red line in the “Leaks” section. The assistant view includes an area where Instruments will show you the stack trace involved in the leak, giving you insights of where the problem could be and even allowing you to navigate directly to the offending code.

retainCycle