When we first started Chocolat in 2009, Leopard was being shipped with new Macs. New in Leopard was Garbage Collection (GC), a technology that removed the burden of writing
-release calls in one’s Objective-C code, and the risk of segfaulting if mistakenly omitted. A child of its time, we used GC to make Chocolat.
It was all going great until when in 2011 Apple unveiled the even nicer Automatic Reference Counting (ARC), and pronounced a death sentence upon GC.
This surprised everybody. The rumour going around at the time was that Apple was going to port GC to the iPhone. Just like Android. The compile-time reference counting scheme they revealed instead was a lot better than any of us could have dreamed about. Unfortunately for me it was also incompatible with GC.
Fast forward to 2013. Chocolat was surviving with GC, albeit illicitly. Apple had said they were going to remove GC at “some point”, but it had already been two years.
But in 2013/14, Apple made two announcements. First, that Xcode had been transitioned from GC to ARC. Second, that Xcode 5.1 would no longer compile GC apps.
Xcode was the only thing keeping GC in OS X alive at that point, and Apple was desperate to get rid of it, so I started planning for Chocolat’s conversion.
In April 2014 I made the decision that Chocolat 2.3 would be the last of its line. Chocolat 3 would use Automatic Reference Counting. I figured I had six months until October when 10.10 was expected.
Now, converting an app from GC to ARC is not done at the flick of a switch. It is nothing like as easy as the switch from PPC to Intel, or the switch from 32-bit to 64-bit.
Memory management touches every line of code, and to convert GC app, you have to do it all at once. All 100,000 lines of Chocolat.
As a lone programmer, I had no hope in hell of doing all of that in six months. I needed help. So I enlisted the overworked programmer’s best friend: Python. I built a suite of tools to parse Chocolat’s code and help with the conversion.
To give you an idea, one of the tools I wrote for the conversion built a graph of memory dependencies between classes. I annotated all NSArrays and NSDictionary properties in Chocolat with type information so that the tool could see inside containers. Then I used a simple algorithm to reduce the graph down so that it contained only the cycles.
Another part of the conversion involved replacing both scripting APIs in Chocolat. We had an internal JS API (“icings”), and an external API (“mixins”). Both relied on the cycle collecting abilities of Objective-C’s GC. Both had to go.
From the ashes of the old mixins was born mixins v2, which runs each extension in its own separate process. This dramatically simplified memory management — to clean up a mixin, just kill its process — and made the overall conversion possible.
All the internal “icings” scripts were rewritten in Objective-C. I made a simple library of common JS idioms implemented in ObjC so that I could convert JS code to Objective-C code without fuss.
One of the most stressful parts was fixing the errors Apple’s “automatic” converter spat out. There were about 3000 of them, or one error every 33 lines. Working at a rate of roughly 100 errors fixed/hour, it took me about a week to fix them all.
Eventually though, after about eight weeks, I had the whole thing working. It was glorious. And man was it fast.
That is why it costs $15.