Apple Developer Connection
Member Login Log In | Not a Member? Contact ADC

< Previous PageNext Page > Hide TOC

Object Retention and Disposal

Beginning with Mac OS X version 10.5, Objective-C gives you two ways to ensure that objects persist when they are needed and are destroyed when they are no longer needed, thus freeing up memory. The preferred approach is to use the technology of garbage collection: the runtime detects objects that are no longer needed and disposes of them automatically. (It also happens to be the simpler approach in most cases.) The second approach, called memory management, is based on reference counting: an object carries with it a numerical value reflecting the current claims on the object; when this value reaches zero, the object is deallocated.

The amount of work that you, as a developer writing Objective-C code, must do to take advantage of garbage collection or memory management varies considerably.

In addition to being easier to implement, garbage-collected code has several advantages over memory-managed code. Garbage collection provides a simple, consistent model for all participating programs while avoiding problems such as retain cycles. It also simplifies the implementation of accessor methods and makes it easier to ensure thread and exception safety.

Important: Although memory-management methods are “no-ops” in a garbage-collected application, there are still intractable differences between certain programming paradigms and patterns used in the two models. Therefore it is not recommended that you migrate a memory-managed application to a source base that tries to support both memory management and garbage collection. Instead, create a new full-version release that supports garbage collection.

The following sections explore how garbage collection and memory management work by following the life cycle of objects from their creation to their destruction.

Further Reading: To learn all about about the garbage-collection feature of Objective-C, read Garbage Collection Programming Guide. Memory management is discussed in detail in Memory Management Programming Guide for Cocoa.

In this section:

How Garbage Collection Works
How Memory Management Works


How Garbage Collection Works

To the garbage collector, objects in a program are either reachable or are not reachable. Periodically the collector scans through the objects and collects those that are reachable. Those objects that aren’t reachable—the “garbage” objects—are finalized (that is, finalize is invoked). Subsequently, the memory they had occupied is freed.

The critical notion behind the architecture of the Objective-C garbage collector is the set of factors that constitute a reachable object. These start with an initial root set of objects: global variables (including NSApp), stack variables, and objects with external references (that is, outlets). The objects of the initial root set are never treated as garbage and therefore persist throughout the runtime life of the program. The collector adds to this initial set all objects that are directly reachable through strong references as well as all possible references found in the call stacks of every Cocoa thread. The garbage collector recursively follows strong references from the root set of objects to other objects, and from those objects to other objects, until all potentially reachable objects have been accounted for. (All references from one object to another object are considered strong references by default; weak references have to be explicitly marked as such.) In other words, a nonroot object persists at the end of a collection cycle only if the collector can reach it via strong references from a root object.

Figure 2-3 illustrates the general path the collector follows when it looks for reachable objects. But it also shows a few other important aspects of the garbage collector. The collector scans only a portion of a Cocoa program’s virtual memory for reachable objects. Scanned memory includes the call stacks of threads, global variables, and the auto zone, an area of memory from which all garbage collected blocks of memory are dispensed. The collector does not scan the malloc zone, which is the zone from which blocks of memory or allocated via the malloc function.


Figure 2-3  Reachable and unreachable objects

Reachable and non-reachable objects

Another thing the diagram illustrates is that objects may have strong references to other objects, but if there is no chain of strong references that leads back to a root object, the object is considered unreachable and is disposed of at the end of a collection cycle. And indeed these references can be circular, but in garbage collection the circular references do not cause memory leaks, as do retain cycles in memory-managed code. All of these objects are disposed of when they are no longer reachable.

The Objective-C garbage collector is request-driven, not demand-driven. It initiates collection cycles only upon request, which Cocoa makes at intervals optimized for performance or when a certain memory threshold has been exceeded. You can also request collections using methods of the NSGarbageCollector class. The garbage collector is also generational. It makes not only exhaustive, or full, collections of program objects periodically, but it makes incremental collections based on the “generation” of objects. A object’s generation is determined by when it was allocated. Incremental collections, which are faster and more frequent than full collections, affect the more recently allocated objects. (Most objects are assumed to “die young”; if an object survives the first collection, it is likely to be intended to have a longer life.)

The garbage collector runs on one thread of a Cocoa program. During a collection cycle it will stop secondary threads to determine which objects in those threads are unreachable. But it never stops all threads at once, and it stops each thread for as short a time as possible. The collector is also conservative in that it never compacts auto-zone memory by relocating blocks of memory or updating pointers; once allocated, an object always stays in its original memory location.

How Memory Management Works

In memory-managed Objective-C code, a Cocoa object exists over a life span which, potentially at least, has distinct stages. It is created, initialized, and used (that is, other objects send messages to it). It is possibly retained, copied, or archived, and eventually it is released and destroyed. The following discussion charts the life of a typical object without going into much detail—yet.

Let’s begin at the end, at the way objects are disposed of when garbage collection is turned off. In this context Cocoa and Objective-C opt for a voluntary, policy-driven procedure for keeping objects around and disposing of them when they’re no longer needed.

This procedure and policy rest on the notion of reference counting. Each Cocoa object carries with it an integer indicating the number of other objects (or even procedural code sites) that are interested in its persistence. This integer is referred to as the object’s retain count (“retain” is used to avoid overloading the term “reference”). When you create an object, either by using a class factory method or by using the alloc or allocWithZone: class methods, Cocoa does a couple of very important things:

After object allocation, you generally initialize an object by setting its instance variables to reasonable initial values. (NSObject declares the init method as the prototype for this purpose.) The object is now ready to be used; you can send messages to it, pass it to other objects, and so on.

Note: Because an initializer can return an object other than the one explicitly allocated, the convention is to nest the alloc message expression in the init message (or other initializer)—for example:

id anObj = [[MyClass alloc] init];

When you release an object—that is, send a release message to it—NSObject decrements its retain count. If the retain count falls from one to zero, the object is deallocated. Deallocation takes place in two steps. First, the object’s dealloc method is invoked to release instance variables and free dynamically allocated memory. Then the operating system destroys the object itself and reclaims the memory the object once occupied.

Important: You should never directly invoke an object’s dealloc method.

What if you don’t want an object to go away any time soon? If after receiving an object from somewhere you send it a retain message, the object’s retain count is incremented to two. Now two release messages are required before deallocation occurs. Figure 2-4 depicts this rather simplistic scenario.


Figure 2-4  The life cycle of an object—simplified view

The life cycle of an object—simplified view

Of course, in this scenario the creator of an object has no need to retain the object. It owns the object already. But if this creator were to pass the object to another object in a message, the situation changes. In an Objective-C program, an object received from some other object is always assumed to be valid within the scope it is obtained. The receiving object can send messages to the received object and can pass it to other objects. This assumption requires the sending object to “behave” and not prematurely free the object while a client object has a reference to it.

If the client object wants to keep the received object around after it goes out of programmatic scope, it can retain it—that is, send it a retain message. Retaining an object increments its retain count, thereby expressing an ownership interest in the object. The client object assumes a responsibility to release the object at some later time. If the creator of an object releases it, but a client object has retained that same object, the object persists until the client releases it. Figure 2-5 illustrates this sequence.


Figure 2-5  Retaining a received object

Retaining a received object

Instead of retaining an object you could copy it by sending it a copy or copyWithZone: message. (Many, if not most, subclasses encapsulating some kind of data adopt or conform to this protocol.) Copying an object not only duplicates it but almost always resets its retain count to one (see Figure 2-6). The copy can be shallow or deep, depending on the nature of the object and its intended usage. A deep copy duplicates the objects held as instance variables of the copied object while a shallow copy duplicates only the references to those instance variables.

In terms of usage, what differentiates a copy from a retain is that the former claims the object for the sole use of the new owner; the new owner can mutate the copied object without regard to its origin. Generally you copy an object instead of retaining it when it is a value object—that is, an object encapsulating some primitive value. This is especially true when that object is mutable, such as an NSMutableString. For immutable objects, copy and retain can be equivalent and might be implemented similarly.


Figure 2-6  Copying a received object

Copying a received object

You might have noticed a potential problem with this scheme for managing the object life cycle. An object that creates an object and passes it to another object cannot always know when it can release the object safely. There could be multiple references to that object on the call stack, some by objects unknown to the creating object. If the creating object releases the created object and then some other object sends a message to that now-destroyed object, the program could crash. To get around this problem, Cocoa introduces a mechanism for deferred deallocation called autoreleasing.

Autoreleasing makes use of autorelease pools (defined by the NSAutoreleasePool class). A autorelease pool is a collection of objects within an explicitly defined scope that are marked for eventual release. Autorelease pools can be nested. When you send an object an autorelease message, a reference to that object is put into the most immediate autorelease pool. It is still a valid object, so other objects within the scope defined by the autorelease pool can send messages to it. When program execution reaches the end of the scope, the pool is released and, as a consequence, all objects in the pool are released as well (see Figure 2-7). If you are developing an application you may not need to set up an autorelease pool; the Application Kit automatically sets up an autorelease pool scoped to the application’s event cycle.


Figure 2-7  An autorelease pool

An autorelease pool

So far the discussion of the object life cycle has focused on the mechanics of managing objects through that cycle. But a policy of object ownership guides the use of these mechanisms. This policy can be summarized as follows:

Conversely,

As with any set of rules, there are exceptions and “gotchas”:

Note: “Release” in the above guidelines means sending either a release message or an autorelease message to an object.

If you do not follow this ownership policy, two bad things are likely to happen in your Cocoa program. Because you did not release created, copied, or retained objects, your program is now leaking memory. Or your program crashes because you sent a message to an object that was deallocated out from under you. And here’s a further caveat: debugging these problems can be a time-consuming affair.

A further basic event that could happen to an object during its life cycle is archiving. Archiving converts the web of interrelated objects that comprise an object-oriented program—the object graph—into a persistent form (usually a file) that preserves the identity and relationships of each object in the graph. When the program is unarchived, its object graph is reconstructed from this archive. To participate in archiving (and unarchiving), an object must be able to encode (and decode) its instance variables using the methods of the NSCoder class. NSObject adopts the NSCoding protocol for this purpose. For more information on the archiving of objects, see “Object Archives”.



< Previous PageNext Page > Hide TOC


Last updated: 2007-10-31




Did this document help you?
Yes: Tell us what works for you.

It’s good, but: Report typos, inaccuracies, and so forth.

It wasn’t helpful: Tell us what would have helped.
Get information on Apple products.
Visit the Apple Store online or at retail locations.
1-800-MY-APPLE

Copyright © 2007 Apple Inc.
All rights reserved. | Terms of use | Privacy Notice