Retain Cycles – The hidden trap of ARC – Part 3

Posted on November 22, 2015

WHAT TO DO ?

Luckily, we don’t have to modify the data structure of our app. Revealing a place where such cycles occurs, especially in a big and complex projects can be quite challenging and that’s our main problem. However, if we’re succeed, the only thing to do  is weaken one of references which creates a cycle.

STRONG AND WEAK REFERENCES

While through the accretions of the past, in Objective-C, hard defined type of the reference was something commonly used and something that anybody could easily encounter, where even the beginner by seeing properties declarations with type, usually also followed by even greater number of other directives, could find that something’s up, all of that is a lot less clear, practically hidden in Swift.

Fig.5 Strong reference declaration in Objective-C.

Fig.5.1 Strong reference definition in Swift.

Basically, there are two types of references – strong and weak. Not for no reason, I strongly emphasized a “retain” word while writing about object references before. Retaining is a domain of strong references and actually, that’s the default behavior. As mentioned before, the ARC is based on reference counting. It counts the number of object references and removing it when this amount reach 0. Besides pointing to the object, strong reference also increments retain counter, which guarantees existence of object as long as at least one reference exists. In other words – strong reference retains the object.

With weak references, things are slightly different. They’re completely transparent to the ARC and all they do is just pointing to the object. This is where a new danger arise – while weak reference is not retain the object, there’s a possibility that it already has been deleted, while we try to use it. It’ll end with crash of course and to avoid it, before we use an object pointed by weak reference, we have to check for its existence. More or less accurate will be a metaphorical representation of the object as the car standing at the traffic lights. A driver who has control over it and decides where and when it go, represents the strong reference. The weak reference would be represented by a pedestrian, who’s starring at the car. He see all the properties, like car color, brand, chassis type etc., but doesn’t have any influence on time, when it disappear from his sight.

All we need to make the reference weak, is “weak” prefix:

Fig.5.2 Weak reference declaration in Objective-C.

Fig.5.3 Weak reference definition in Swift.

Because weak reference can point to non-existing object, which means, can return “nil”, in Swift it must be marked as an optional variable. With proper changes of our hotel application, about which we discussed before, it should now look as shown:

Screen Shot 2015-11-21 at 8.18.11 PM

Fig.6 Corrected references structure of hotel app.

From now on, the room has a reference to it’s tenant, but now, it’s not retains him. Thanks to this, when we back to the menu now, the room and tenant objects will be released and disposed from memory.

Unfortunately, it’s really hard to say which reference should be weaken. It strictly depends on the app structure and dependencies between objects. If you have a good knowledge of how your application works, it should not be a big deal. Usually you can ask a question, which object must exists to give a sense of another object existence and pick a reference to the first one.

I need to mention that there’s one more, rarely used type of weak reference, and it comes with a certain danger. This type is known as “unsafe_unretained” in Objective-C and “unowned” in Swift.

Fig.7 Unsafe weak references.

What’s so unsafe here ? In contrast to weak reference, those are not nulled/nilled when object is removed. It means, that after object disposal, they will keep pointing to the particular memory area where object were stored (please note, that it isn’t marked as optional in Swift). While object is no longer exists, the content of this area is unpredictable and using such reference will immediately crash your app. If you decide to use one of those, you must remember one thing: You have to be absolutely sure, that the object will exists as long, as you want to use this reference. Practically, I’ve never seen any serious reason to use those references and based on that, I would recommend you to always use weak.

VARIOUS CASES OF RETAIN CYCLES OCCURENCE

When you try to google for “retain cycle”, you’ll find plenty of materials. From short articles to entire discussions on various message boards. There’s a one problem. Very often answers are mutually exclusive or problem description ends with one simple example (usually something similar to the hotel app mentioned above).

In this article, I’ll try to cover a few different scenarios by providing detailed description and solution to each, telling you not only what’s causing cycles, but also what definitely not. Every example below will be synthetic to let you focus only on main problem and they’ll be written mostly in Swift, but the same rules apply to the Objective-C. Practical example in form of downloadable source code will be provided and covered later.

CASE #1 – MUTUAL OBJECTS RETAINS

That’s the case we’ve talked earlier. Here I need to mention, that we don’t even need a plural number of instances to make retain cycle occur. It’s possible to create a self-retaining object.

Fig.8 Object self-retaining code.

A code like this will cause a leak. Therefore you shouldn’t create a strong reference to object itself. Of course it’s pointless, but worth mention.

Another situation, which makes debugging process little harder is a case, when object is retained by references of its superclass type. Be aware of it, while it can save a lot of time.

Fig.8.1 Synthetic code showing a complex loop of retains.

Presented on Fig 8.1, ClassC object is retained by a reference of type SuperClassA, which in case of real, more complex code can lead into wrong suggestion, that this particular reference has nothing in common with the cycle.

The leak can be also caused by passing weak reference into strong reference.

Fig.8.2 There’s no expected result of weakening “me” reference, because by passing it into strong reference, the retain counter is incremented.

SOLUTION

All you need to do is weakening one or more references involved in retain cycle by prefixing them with “weak” or “unowned” directive. There’s no universal solution which reference should be weak. It strongly depends on program structure and dependencies.

CASE #2 – THE DANGEROUS (STORED) CLOSURES / BLOCKS

Closures are very powerful and widely used tool. They let you pass a whole block of code as method parameter and execute it in completely different scope. Closure is a special type of object and like any object it can be pointed by a reference (while writing it, in Swift 2.1, only strong reference). Therefore it’ll be stored as long as objects which retains it exists.

To be able to comprehend how closure usage can lead into retain cycles, let’s take a look at Apple’s book, “The Swift Programming Language”. The part which interest us most, is one of first sentence, opening the closure’s chapter:

Closures can capture and store references to any constants and variables from the context in which they are defined.”

In practice, it means that every constant or variable from current scope which were used in closure, will be captured and retained by it as long as closure exists.

Fig.9 Difficult to find at glance case of retain cycle caused by reference captured and retained by the closure.

Because the task of closure passed into “theClosure” property of SampleClassA is an execution of SampleClassB object method, “theMethod()”, to be able to do it at any time, closure need to capture and retain SampleClassB object. In different words, it’s capturing the “self” of SampleClassB object in example above. From now on, while SampleClassA object retains a closure which retains SampleClassB object, which in turn keeping reference to SampleClassA object, we have an unwanted loop here. There’s a big informational mess about this topic in the internet, and let’s say clear here: This kind of objects relation is definitely causing retain cycle.

SOLUTION

It’s really simple. Inside closures which are stored, you need to weaken the self reference by using “[weak self]” or “[unowned self]” directives in Swift and a weakSelf trick in Objective-C.

Fig.9.1 Solution for closure’s/block’s “self” capturing in Swift and Objective-C.

The same way you can use “[unowned self]” directive in Swift or “__unsafe_unretained” instead of “__weak” in Objective-C, but in case of closures and blocks, it’s extremely unsafe and should be avoided.

Do we always must use weak self reference while working with closures/blocks ? Of course no, and I will cover it later.