< Previous PageNext Page >

The Life Cycle of a Cocoa Object

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. Unlike some other programming languages, Objective-C has no “garbage collection” facility that frees objects automatically when they’re no longer needed. Instead of the overhead and inflexibility of garbage collection, 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 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 creating an object 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-3 depicts this rather simplistic scenario.


Figure 2-3  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-4 illustrates this sequence.


Figure 2-4  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-5). 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-5  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-6). 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-6  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”.

Further Reading: This overview of a Cocoa object’s life cycle skims the surface of what there is to say about the subject. For a more detailed discussion of issues relating to memory management and Cocoa objects, see “The Objective-C Runtime System” in The Objective-C Programming Language. Also see the programming topic Memory Management Programming Guide for Cocoa.



< Previous PageNext Page >


© 2006 Apple Computer, Inc. All Rights Reserved. (Last updated: 2006-12-20)


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.