Now serving...

ARC

Written by Jordan Morgan • Jan 9th, 2016

If I’m being honest, I feel a little bit spoiled. When I first began my journey into the endless world of software development, I was already afforded several niceties that I wasn’t even aware of. Chief among them? Memory management.

C# and the .NET framework swept up any loose ends automatically with garbage collection. With iOS 6, keeping reference counts was a thing of the past. Managing memory was something I was aware of early on, but it was more out of sight, out of mind.

But hey, the more you know, amirite? Today we’ll talk ARC and it’s relationship with Swift.

The ARC of the Compiler

For the uninitiated, ARC stands for automatic reference counting. It’s sole job is to free up objects when there are absolutely zero references left to them.

The emphasis here is on objects, so only reference types deal with ARC. This is something to keep in the ol’ hip pocket, because if you recall, Swift really, really loves value types.

When one starts to understand what ARC is and its job, some legacy Objective-C code might start to make a little more sense:

NSObject *box = [NSObject new];

//More code, and then later on…

[box release];

The release message being sent is essentially informing the compiler that, in the current scope, box is no longer needed and its reference to it is being released. This would mean that box’s retain count just went down by one, and when it would reach zero, well — it would enter into the dark ether never to be seen again…

…that is, unless you tried to send box a message right after it had already been released. In a lot of cases, one could be warmly greeted with a nil reference exception.

Bottom line, one had to really think about their references. Why? Because it was up to the developer to manage them. While that’s not such a bad thing, it certainly wasn’t convenient and it could easily crumble the novice would-be iOS developer. All of this legwork being handled automagically is what ARC was created for.

Clean Up, Aisle 0XA4F65

I’d wager that most new developers who take up iOS have no idea what’s likely happening in memory. That’s certainly not a dig, it’s just insanely easy to miss now. Cupertino & Co© have ensured that ARC cleans up after developers and allows us to focus more on the implementation. I, for one, dig that.

Alas, there will eventually come a time when memory management, references, etc. do matter. If one hasn’t the slightest idea about the topic, these issues are particularly hard to debug. I may or may not (but definitely do) maybe, possibly know this from firsthand experiences.

With that, take this class into account:

class Post  
{  
    var topic:String
    
    init(t:String)  
    {  
        self.topic = t  
        print("A post lives!")  
    }
    
    deinit  
    {  
        print("A post has passed away 💀")  
    }
}

Since Post is a reference type, when a variable is assigned a new instance of a Post object it will automatically be given a strong reference to it. Most will be right at home with this, since a vast majority of property declarations in iOS written with Objective-C included the strong attribute. This means ARC will handle things like so:

//An optional of type Post, so the default value is nil so far  
var postRef:Post?

//Console prints 'A post lives!', postRef has a strong ref  
postRef = Post(t: "iOS")

//This instance of Post now has another strong ref, so its  
//retain count now sits at 2  
var anotherPostRef = postRef

//postRef equals nil, one strong ref is gone. Retain count is 1  
postRef = nil

//ARC knew it still had a strong ref due to keeping a retain count.  
//Thus, anotherPostRef is *not* nil  
print(anotherPostRef)

With code samples like the previous, I find it’s easiest to just read the comments line by line. I’ll also not lie and say I had a devious grin on my face while authoring that code. Memory management questions were, and still are, always a surefire way to humble a classroom full of fledgling software developers.

I’ll Free You

Keeping with the last code sample, one could see how things ‘just worked’ because ARC kept up with the retain counts to the Post reference like a true scholar. As much as ARC saves, thus, it destroyith as well. Here is the same code with one added line, sans previous comments:

var postRef:Post?

postRef = Post(t: "iOS")
var anotherPostRef = postRef

postRef = nil
print(anotherPostRef)

//'A post has passed away 💀' is printed to the console. ARC saw   
//that another strong reference was released to the POST instance.  
//Now, the retain count is 0 and the memory is freed.  
anotherPostRef = nil

The Gotcha’

Now, we arrive at the “more you know” section of the post. If you’ve gone this far in your Swifting without having to worry about memory, bravo. But, should you come across Class declarations like this, beware:

class Post  
{  
    var topic:String  
    var performance: Analytics?
    
    init(t:String)  
    {  
        self.topic = t  
        print("A topic lives!")  
    }
    
    deinit  
    {  
        print("A topic has passed away 💀")  
    }  
}

class Analytics  
{  
    var thePost:Post?  
    var hits:Int
    
    init(h: Int)  
    {  
        self.hits = h  
        print("Some analytics are being served on up.")  
    }
    
    deinit  
    {  
        print("Analytics are gone!")  
    }  
}

The quandary that could be created from these classes stem from the fact that each could potentially hold a strong reference to one another:

var aPost:Post? = Post(t: "ARC")  
var postMetrics:Analytics? = Analytics(h: 3422)

aPost!.performance = postMetrics  
postMetrics!.thePost = aPost

//Ruh roh, aPost's deinit func wasn't called!  
aPost = nil

…and the reason the deinit method wasn’t called for the Analytics instance is due to a strong reference cycle, wherein one has coded themselves into a cold, dark corner such that an instance could never have a retain count of 0. But hey, at least you’ll never be alone.

If the example was a bit fast, no worries, the explanation is incredibly simple. In the last bit of good ol’ Swift code, postMetrics still had a strong reference to aPost due to this line:

postMetrics!.thePost = aPost

So, even though we assigned aPost to nil, the postMetrics instance holds on to its reference of it. And it won’t let go, hell or high water.

The fix? Well, in theory, it’s assuring that should a situation like this be possible — make sure that it’s impossible for one of the instances to still hold a strong reference to the other.

If we busted out one of Swift’s many property keywords, this would be fixed up. In this scenario, we might rewrite Analytics’s thePost property as such:

weak var thePost:Post?

Then, if we were to run the exact same code in your friendly neighborhood Xcode playground, both instances would be deallocated as they should. Also, due to their weak nature (haha), all weak properties must be declared as a variable (i.e. not using let) to denote to the compiler that its value can certainly mutate.

Wrapping Up

ARC is an incredible technology. When I was first starting out, it gave me that extra bit of confidence I needed to start developing iOS/OS X software. As the days passed and my viewcontrollers grew (hey — I said the early days), I began hitting some walls due to some ill property choices.

Though this information is far from groundbreaking, my hope is that someone else who is just starting out might have stumbled upon this post. Walk away with your chin up, oh ye novice Swifter, because memory mangement isn’t scary. In most cases, it’s downright easy thanks to your close pal ARC.

Until next time ✌️.

Spot any corrections or have anything to add?
Feel free to open an issue or create a pull request for this article.