Thursday, September 11, 2008

Memory management on the iphone

Cocoa does not use garbage collection. You must do your own memory management through reference counting. Following a few simple rules can make memory management easy. Failure to adhere to the rules will almost certainly lead at some point to memory leaks, or runtime exceptions due to messages being sent to freed objects.

One feature of the Cocoa environment is its facility for managing dynamically allocated memory. Cocoa's NSObject class, from which most classes, both vendor and user, are derived, implements a reference counting scheme for memory management. Objects derived from the NSObject root class respond to a retain and a release message and keep a retain count which can be queried by sending a retainCount message. A newly allocated object created with alloc or copy has a retain count of one. Sending that object a retain message increments the retain count, while sending it a release message decrements the retain count. When an object's retain count reaches zero, it is deallocated, its memory freed. (Deallocation is to Objective-C objects as destruction is to C++ objects. The dealloc method is functionally similar to a C++ destructor. dealloc is not guaranteed to be invoked.) This reference counting approach is very similar to that of Microsoft's COM which features the IUnknown interface. IUnknown provides equivalents to retain and release in the form of AddRef and Release.

In addition to manual reference counting, application programmers may choose to make use of autorelease pools. Sending an object an autorelease message registers a future release with that thread's nearest autorelease pool. When the autorelease pool is itself released, it sends a corresponding release message for every registered autorelease. Autorelease pools are generally created and released at the beginning and end of the event loop, guaranteeing program flow has passed out of the block where objects were autoreleased. This means the application has predictable performance and memory collection is generally invisible to the user, whereas under most fully automated schemes the application will sometimes suddenly stop responding when the garbage collection system is started.

Automatic garbage collection for Cocoa is also supported starting with Objective-C 2.0, using Xcode 3.0 which is included with Mac OS X 10.5 Leopard. The programmer now has the choice of whether to manually manage memory of objects or not. Opinions on this are divided. Some say that reference counting is superior because it allows the programmer to have precise control over when objects are deallocated, but does not add the burden of doing so for every object a program allocates, nor incur the performance penalty usually associated with automatic garbage collection. Others say the entire scheme is unnecessary, that Java-style automatic garbage collection is superior, because it significantly reduces the possibility of programmer error in memory management. Cocoa garbage collection is fully backwards compatible; it is only used if the project settings are modified to take advantage of it. Furthermore, garbage collection can be turned on or off at different points in the program.

Memory management basics

To keep memory consumption as low as possible in an application, you should get rid of objects that are not being used, but you need to make sure that you don’t get rid of an object that is being used. You therefore need a mechanism that allows you to mark an object as still being useful. In many respects, memory management is thus best understood in terms of “object ownership.”

  • An object may have one or more owners.

    (By way of an analogy, consider a timeshare apartment.)

  • When an object has no owners, it is destroyed.

    (To stretch an analogy, consider a timeshare complex that is not loved by the local population. If there are no owners, the complex will be torn down.)

  • To make sure an object you’re interested in is not destroyed, you must become an owner.

    (You can either build a new apartment, or take a stake in an existing one.)

To support this model, Cocoa provides a mechanism called “reference counting” or “retain counting.” Every object has a retain count. An object is created with a retain count of 1. When the retain count drops to 0, an object is deallocated (destroyed). You manipulate the retain count (take and relinquish ownership) using a variety of methods:

alloc

Allocates memory for an object, and returns it with retain count of 1

You own objects you create using alloc.

copy

Makes a copy of an object, and returns it with retain count of 1

If you copy an object, you own the copy.

retain

Increases the reference count of an object by 1

Takes ownership of an object.

release

Decreases the reference count of an object by 1

Relinquishes ownership of an object.

autorelease

Decreases the reference count of an object by 1 at some stage in the future

Relinquishes ownership of an object at some stage in the future.

The following rules apply:

  • Within a given block of code, the number of times you use copy, alloc and retain should equal the number of times you use release and autorelease.

  • You only own objects you created using a method whose name begins with “alloc” or “new” or contains “copy” (for example, alloc, newObject, or mutableCopy), or if you send it a retain message.

  • Implement a dealloc method to release the instance variables you own.

  • You should never invoke dealloc directly (other than when you invoke super’s implementation in a custom dealloc method).

Many classes provide methods of the form +className... that you can use to obtain a new instance of the class. Often referred to as “convenience constructors”, these methods create a new instance of the class, initialize it, and return it for you to use. You do not own objects returned from convenience constructors, or from other accessor methods.

See the documentation on memory management


Memory Management Rules

This document summarizes the rules for memory management in Objective-C.

This is the fundamental rule:

  • You take ownership of an object if you create it using a method whose name begins with “alloc” or “new” or contains “copy” (for example, alloc, newObject, or mutableCopy), or if you send it a retain message. You are responsible for relinquishing ownership of objects you own using release or autorelease. Any other time you receive an object, you must not release it.

The following rules derive from the fundamental rule, or cope with edge cases:

  • As a corollary of the fundamental rule, if you need to store a received object as a property in an instance variable, you must retain or copy it. (This is not true for weak references, described at “Weak References to Objects,” but these are typically rare.)

  • A received object is normally guaranteed to remain valid within the method it was received in (exceptions include multithreaded applications and some Distributed Objects situations, although you must also take care if you modify the object from which you received the object). That method may also safely return the object to its invoker.

    Use retain in combination with release or autorelease when needed to prevent an object from being invalidated as a normal side-effect of a message (see “Validity of Shared Objects”).

  • autorelease just means “send a release message later” (for some definition of later—see “Autorelease Pools”).

For a more complete discussion of memory management in Objective-C see “Object Ownership and Disposal.”