Retain cycles — The hidden trap of ARC, part II
The second part of “Retain cycles — the hidden trap of ARC”. If you would like to check part I, click here.
ARC TO THE RESCUE!
To prevent the aforementioned situation, Apple introduced, still supported, developed and also implemented in Swift, the MRR extension, where whole procedures of retaining and releasing references are automated. This mechanism, known as ARC (Automatic Reference Counting) allowed programmers to almost entirely focus on the functionality by freeing them from memory management designing necessity.
ARC works well, especially in environments with a limited amount of memory, such as mobile phones, but in comparison with TGC (Tracing Garbage Collection) it’s not entirely maintenance-free and the word “almost” used in the last sentence is not a coincidence.
Paraphrasing the words of former Apple CEO, Steve Jobs — There is one more thing…
Like any other retain count based memory management system, ARC has a specific ailment, which many programmers, especially beginners don’t even know about. In most cases — for a limited time. Here comes the moment — especially while working on a bigger project, when perfectly working app suddenly close, no one knows why and the most terrible thing is, that the debugger window where we used to look for the answers… is empty. If we’re little more lucky, it’ll throw a memory warning which actually isn’t anything special, but it lead us to the right way of thinking — we’ve got a memory problem. Something’s leaking here.
The aforementioned ailment of ARC is its susceptibility to the situation, where two different objects retain each other. This kind of loop, called retain cycle, is a frequent cause of strange behavior, crashes, or performance issues, especially when problem grows along with the time of app usage. Unfortunately, you don’t even have to try to make this error occur. It’s just a matter of a while of inadvertence (not even mentioning the lack of awareness that such phenomenon exists) and sooner or later we will fall into trouble.
To illustrate what retain cycle really is and how it is created, I will use an example. Let’s say, that we are writing a hotel managing program, which holds information about its rooms and people living in them. Then we have two classes — HotelRoom and RoomTenant. Every room, being an object of HotelRoom class, should contain information about the current tenant. The tenant, however, as a RoomTenant class object, to make few of it’s methods work, need information about the currently rented room.
In this situation, one of the room’s parameters is a reference to the object of the tenant and analogically, a tenant possess a reference to the rented room. Furthermore, the references to both objects are retained by the view controller, which is responsible for displaying the list of rooms and tenants. This is how it looks:
Fig.3 The scheme representing relations between objects in mentioned hotel management app.
To understand this situation, I propose to read this scheme in following way: The upper blocks are references contained in currently running code of our program, lower blocks, on the other hand, are the memory storage area, where objects exist physically.
At this moment, the amount of references which retain each object is equal to 2. The program runs along and at the time when we, for example, went out to the menu screen, along with released and in consequence deleted list view controller, references to tenant and room’s objects are also released, because they’re no longer needed.
Now, it looks like this:
Fig.3.1 The scheme representing retain cycle between objects in mentioned hotel management app.
After this, each object reference count decreases to 1. Then when will those objects get released? The correct answer is — never. Although there are no references to any of these instances in the currently executed code and they, in effect, will never be used, they still exist in the memory, wasting it. Because each room holds a tenant through a reference, and each tenant holds room, the retain count of each will never reach 0.
This problem, unfortunately, does not always look that easy. In reality, loops like these can take a lot more complicated forms, with no direct references between retained objects as shown at Fig.4.
Fig.4 Retain loops in complex relations structure.
The last part of “Retain Cycles — The hidden trap of ARC” coming soon!