Garbage Collection, ARC, and managing memory

This is a preliminary version of a new chapter on Memory Management that will appear in the upcoming fourth edition of AppleScriptObjC Explored. It addresses changes introduced with Xcode 4.4 and with OS X 10.8 Mountain Lion. The new edition will be available as an upgrade to those who have bought the book, and will be free to recent purchases and those who buy between now and its release. Details on other new content will be announced shortly.

MANAGING memory use is one of the basic requirements of an application. Acquiring memory as required is easy, but there also needs to be a way of freeing memory that is no longer required. Without such a process, an application will consume more and more memory, and eventually swamp the system.

For AppleScript users, and AppleScriptObjC users, the process is generally handled for you. Although that is most of what you need to know, it is useful to understand how it is handled in Objective-C, if only to help you make sense of the documentation and sample code. It is also important to be aware of a big change introduced in OS X 10.8 Mountain Lion.

In some languages, such as AppleScript, memory management is handled automatically by a process known as garbage collection. In essence, you keep using memory as you need it, and periodically another process is run to free memory that is no longer needed. Because most AppleScript applications are one-shot — they perform a process then quit — garbage collection may not even be triggered. With applications that stay open, it obviously becomes more of an issue.

In Objective-C, memory management has traditionally been handled by a process known as reference counting. To simplify, all values have a reference count, sometimes called a retain count. When you create an object using the alloc method (or a method whose name begins with new, copy or copyMutable), it has a count of 1. Any count above 0 means the memory will be reserved for that value, but when it drops to 0, the memory will be freed.

So typically a programmer can create an object using alloc and init, use it, and when finished with it they can call the release method. This decreases the count by 1, and if that takes it to 0, the memory is freed.

An object's reference count can be more than 1. For example, if you have an object with a count of 1 and you add it to an array, its count increases to 2. If it is removed from the array, or the array is deallocated, it will decrease by 1. And a programmer can also increase the value by using the retain method, which also increments it by 1.

Complicating things is the concept of autoreleasing. An autoreleased object is one that that has been released, and therefore its memory can be freed, but which will will not be freed immediately. This is very convenient for values that are only required for a short time — it effectively lets you release them ahead of time, and let whatever you are passing them to take control. Most of the objects produced by what are sometimes called convenience methods — for example, using stringWithString: instead of alloc and initWithString: — return autoreleased objects. If a programmer needs to keep these objects around, he or she will call the retain method. A programmer can also call the autorelease method instead of release on an object, to free its memory at some point in the future.

Although this form of memory managment is implemented via reference counting, programmers are encouraged to think not in terms of the count, but in terms of what is called ownership. The idea is that if you use a method beginning with alloc, new, copy or mutableCopy, you become an owner of the resulting object, and you are responsible for releasing it when you have finished with it, using the release or autorelease method. Otherwise you do not become an owner unless you call retain.

An object can have more than one owner. For example, if a programmer creates a string using alloc and init and adds it to an array, the array, too, owns the object. The object's memory is freed only when all owners relinquish their ownership.

This is simplifying the rules somewhat; they are explained in full in the documentation. But hopefully it will help you understand what the use of the retain, release and autorelease methods in sample code is there for.

If this all sounds a bit fraught, well it is. Memory management bugs are very common. The most common are memory leaks: where programmers fail to release objects when they have finished with them. Memory use increases over time; with bad leaks this can happen quickly, with catastrophic results. Minor leaks are not at all uncommon, although fortunately virtual memory means they are not as problematic as they would otherwise be.

The other type of memory bug can be more insidious: it happens when an application tries to access a value whose memory has already been freed. These are much harder to track down because they can seem random — the result can depend on whether the freed memory has been used since, and if so what it contains.

In OS X 10.5, Apple introduced an alternative method of memory management, its own garbage collection scheme. Essentially, this takes care of memory management for the programmer. There are still some issues the programmer has to deal with, which are not generally relevant to AppleScriptObjC users, but the result is that you can essentially forget about the whole business of retain counts and the retain, release and autorelease methods. Applications periodically run a separate process that looks for memory no longer in use, and frees it.

AppleScriptObjC projects before OS X 10.8 have always used garbage collection (although they didn't have to; it was just much easier).

But garbage collection is not without its issues. In particular, it was unsuitable for more constrained systems such as iOS, where it was never implemented.

During the development of OS X 10.7, Apple came up with a new approach to memory management, called Automated Reference Counting, or ARC. Under the hood, it is the same reference-counting mechanism as before, but it is built around a smarter compiler. Essentially, the compiler figures out where the retain and release calls are required in the programmer's code, and inserts them invisibly at compile time. Not only are calls to retain, release and autorelease unnecessary under ARC, they are in fact forbidden.

ARC provides many of the advantages of garbage collection, and avoids some of the drawbacks. Programmers still have control over the lifetime of objects, but they specify it differently.

This makes three options: garbage collection on, garbage collection off and manual reference counting, and garbage collection off and ARC.

In Mountain Lion, Apple has gone a step further: garbage collection has been deprecated, and will be discontinued in a future realease of the OS. Clearly, Apple sees ARC as the way of the future.

How does this affect AppleScriptObjC?

First and foremost, existing applications will continue to work as before, at least for the life of OS X 10.8. If you modify an existing project in Xcode under Mountain Lion, it will still continue to use garbage collection unless you set it to do otherwise.

However, the runtime code behind AppleScriptObjC works differently in OS X 10.8 than in earlier versions of the OS: it takes care of its memory management within itself. This means you can write an application using the Mountain Lion SDK, and it will work whether you specify garbage collection, ARC on, or ARC off — but only when run under 10.8. If you use ARC, an application will leak memory like a seive under earlier versions of the OS, and the same will happen if you use manual memory management unless you include the required retain/release/autorelease calls in your code.

The bottom line is that if you want an application to run in versions earlier than Mountain Lion, you generally need to use garbage collection.

If you start a new project in Xcode 4.4, the default build currently specifies ARC off and garbage collection off (the default has varied). From an AppleScriptObjC coding point of view, there will be no real difference in how you code.

For most people, the desire to support previous versions of the OS will mean sticking with garbage collection for the time being. They should, however, do so with an eye to having to change at some stage in the future. This probably means being cautious in terms of extra Objective-C frameworks and classes they use.

In Xcode 4.3 and earlier, you specify whether garbage collection is used by looking in the Build Settings for the compiler entry Objective-C Garbage Collection, which you set to Required. In later versions of Xcode, this does not appear. If you open an existing project that uses garbage collection, it will be listed at the end of the Build Settings as GCC_ENABLE_OBJC_GC, with a value of required. If you wish to enable it, you will need to turn the compiler setting for Objective-C Automatic Reference Counting to No, choose Add User-Defined Setting from the Add Build Setting menu at the bottom-right of the bottom area (the + sign in the black circle), type GCC_ENABLE_OBJC_GC, and enter a value of required.