Swift was released to the public at the WWDC 2014 to general surprise. The language has since revolutionized the iOS/Mac OS X programming scene, due to its modern style and the numerous improvements over its predecessor, Objective-C. However, one of the big disappointments for me was the decision from Apple to stick with ARC (Automatic Reference Counting) for the memory management in Swift’s runtime.

I understand why Apple has done this. They want the code of Swift and Objective-C to be binary-compatible, avoid having to re-design and build a memory management system, and reduce the adoption hassle as much as they can, but I was expecting a brand new, modern solution for memory management in Cocoa, one that would not require extra work for the developer and would eliminate one of the main drawbacks that Objective-C has been carrying since the pre-ARC times.

Manual Retain Release

A long long time ago (well, not that much, actually ;), before ARC, Cocoa’s memory management was done by means of 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.

The special method alloc was used to create an object and claim ownership over it, while retain was used to claim ownership on an already existing object. On the contrary, release was used to relinquish ownership on the object. There were another methods, like copy, that would create a copy of an existing object (claiming ownership on the newly created object) and autorelease, that was a release that deferred the object’s destruction until it was suitable (typically during the dealloc of the object). Your typical class instantiation would look something like:

Person * myPerson = [[Person alloc] init];
...
[myPerson someMethodOnPerson];
...
[myPerson release]; // when myPerson was no longer needed.

This required a lot of extra work for the developer, specially when dealing with complex classes and interactions between objects, andwas the main cause of memory leaks and memory-related problems in the code. In my opinion, one of the main reasons Objective-C has always been seen as a difficult or unfriendly programming language, is its memory management system.

Enter Automatic Reference Counting

Automatic Reference Counting (ARC) was introduced with XCode 4.2, and was seen as wonderful news by the developers community. You no longer needed to remember to retain and release your objects, ARC would take care of that. You would just need to declare a property as “strong” (meaning that the object will be retained and its reference count increased) or “weak” (meaning that it won’t retain and extend the object’s lifecycle). The problem? ARC is prone to retain cycles, and has never been very good at taking care of memory cleaning.

A retain cycle is caused by two objects having strong references 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.

retainCycleIn order to avoid this circumstance, the developer must be aware of retain cycles in the code and debug the apps (using Instruments, for example) in search of leaks and strong retain cycles. In Objective-C, this implies being careful about whether to define an object’s property as strong or weak.

Another problem of ARC is its indeterminacy. You have no control over when a variable is going to be released and its memory freed. In order to help the developer to handle the memory allocated for objects, ARC defines structures of code called “autorelease pools”. An autorelease pool is an area where you declare objects that would get allocated by ARC, and then, after the execution reaches the end of the autorelease pool, all allocated objects would (hopefully) be released. The problem is that ARC usually defines one main autorelease pool in the project, basically enclosing the main function. That means that, unless you take care of memory management, and define another autorelease pools, the memory of your program can grow without control, potentially reaching a very high memory imprint on the system. You can assign an object to nil, thus indicating ARC that you are releasing that object because you no longer need it, but you cannot really infer when (and if) ARC decides to actually free that object.

Apple has some guidelines regarding the use of autorelease pools to reduce the memory impact on loops with heavy creation/use of objects and memory, but in the end, I have the feeling when programming in Cocoa/Objective-C that I don’t really have control over the memory that my program is using.

What other languages do

When I was a heavy C/C++ programmer, one of my main complaints about the language was the memory management. Much has been written about malloc and free, and their misuse is actually after the vast majority of security bugs, vulnerabilities and nasty exploits found in software today. In C/C++, you are responsible for allocating and freeing the memory of your objects, and you must make sure to allocate exactly the amount of memory you need, and to avoid deallocating an object that’s going to be used later (causing a use-after-free vulnerability) or freeing a memory that’s already been freed before (causing a double-free vulnerability). However, in C/C++ you have full control on when and how your objects get deallocated, which allows you to fine-tune the memory usage of your application.

Other languages like Java, Python, or Ruby implement a mechanism called Garbage Collection to handle the memory management. The garbage collection mechanism usually implies having a reference counting mechanism similar to the one used by ARC, but implementing an additional cycle detection that allows the developer to forget about memory management and just focus on programming the application. In the end, isn’t it what a programming language is supposed to do?

Why Swift should drop ARC

In Swift, the problem with ARC is even worse because Swift is strongly typed and type safe, so all variables must have a known type and non-nil value (unless declared optional). That forces the developer to add some nasty (in my opinion) hacks that have nothing to do with the actual application code. Let’s take a look at the last use case of the memory management chapter from the book The Swift Programming Language. In this chapter, several scenarios where crossed strong references cause a retain cycle are considered, and the last one deals with two entities that have a reference to each other, none of them able to contain a nil value: a Country object that must have a capital City entity, and a City object belonging to a Country. Both of them must have valid values for their properties (capitalCity for a Country, and country for a City object), so City defines country as a unowned variable, and Country defines capitalCity as a non-nil optional variable (?).

class Country {
    let name: String
    let capitalCity: City!
    init(name: String, capitalName: String) {
        self.name = name
        self.capitalCity = City(name: capitalName, country: self)
    }
}
 
class City {
    let name: String
    unowned let country: Country
    init(name: String, country: Country) {
        self.name = name
        self.country = country
    }
}

Now this definition of classes seems quite forced and unnatural to me, and the concept of an optional variable (by definition: a variable that can contain a nil value) that must be non-nil is really hard for me to grab as a logical, useful entity. It is unfortunate because there are many situations in which a relationship between classes can result in a retain cycle, so I would have to get used to this scheme, but I find it really hard to understand and to justify.

I would find it easier to stick to a manual memory management in the C/C++ style. Sure, it is harder, but at least the allocation-release sequence makes sense from a logical point of view. Other languages like Java have included a modern garbage collection mechanism with cycle detection algorithms for years, and I wonder why Apple has decided to loose such an opportunity for changing a problem they have been carrying for years now.