< Previous PageNext Page > Hide TOC

Object Ownership and Disposal

This document discusses the policy for ownership of Objective-C objects and how and when to dispose of objects.

To fully understand how the object ownership policy is implemented in Cocoa, you must also read “Autorelease Pools.”

Contents:

Object Ownership Policy
Delayed Release
Taking Ownership of Objects
Deallocating an Object
Resource Management
Summary


Object Ownership Policy

In an Objective-C program, objects are constantly creating and disposing of other objects. It is important to dispose of objects when they are no longer needed to ensure that your application does not use more memory than necessary. Much of the time an object creates things for private use and can dispose of them as needed. However, when an object passes something to another object through a method invocation, the lines of ownership—and responsibility for disposal—blur. For example, suppose you have a Thingamajig object that contains a number of Sprocket objects, which other objects access using the following method:

– (NSArray *)sprockets

This declaration says nothing about who should dispose of the returned array. It is reasonable to suggest, however, that if your Thingamajig object returns an instance variable, it is responsible for the array. If on the other hand you create a new Thingamajig object, then you are responsible for disposing of the new object. This, though, introduces a possible source of confusion. “Disposal” tends to imply “get rid of” or “deallocate”.

As noted earlier, it is possible (in fact common) for one object to create another object and then pass it to another. It is important not to get rid of the new object until the third party has finished using it. It is better, therefore, to think of memory management in terms of object ownership, where any object may have one or more owner. So long as an object has at least one owner, it continues to exist. If an object has no owners, the runtime system disposes of it (deallocates it) automatically.

To make sure it is clear when you own an object and when you do not, and what responsibilities you have as an owner, Cocoa sets the following policy:

This policy applies both to GUI-based Cocoa applications and to command-line Foundation tools.

Consider the following example:

Thingamajig *thingamajig = [[Thingamajig alloc] init];
// ...
NSArray *sprockets = [thingamajig sprockets];
// ...
[thingamajig release];

This example properly adheres to the policy. You create the Thingamajig object using the alloc method, so you subsequently send it a release message. You obtain the sprockets array from the Thingamajig object—you do not “create” the array—so you do not send it a release message.

Creating Objects Using Convenience Methods

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. Although you might think you are responsible for releasing objects created in this manner, that is not the case according to the policy Cocoa set—the method name does not contain "alloc" or "copy", or begin with "new". Because the class creates the new object, it is responsible for disposing of the new object. As an illustration, the following code example is wrong:

Thingamajig *thingamajig = [Thingamajig thingamajig];
[thingamajig release]; // wrong

Although if you try this you will not see an error as soon as the release message is sent, it will cause an exception later (for a discussion of what constitutes “later”, see “Autorelease Pools”).

This does, though, raise the issue of how the Thingamajig class can abide by the ownership policy. It is responsible for releasing the new object, but it must not do so before the recipient has had a chance to claim ownership. To illustrate, consider two possible implementations of the thingamajig method.

  1. This is wrong because after the new Thingamajig object is returned to the caller the class loses its reference to the new object so cannot send it a release message to relinquish ownership:

    + (Thingamajig *)thingamajig {
        id newThingamajig = [[Thingamajig alloc] init];
        return newThingamajig;
    }
  2. This is also wrong because although the class properly relinquishes ownership of the new object, after the release message is sent the new Thingamajig object has no owner so is immediately disposed of by the system:

    + (Thingamajig *)thingamajig {
        id newThingamajig = [[Thingamajig alloc] init];
        [newThingamajig release];
        return newThingamajig; // newThingamajig is invalid here
    }

The Thingamajig class needs a way to mark an object for relinquish ownership at a later time, after the recipient has had a chance to use it. Cocoa provides a mechanism to do this, called “autoreleasing”, discussed in “Delayed Release.”

Objects Returned by Reference

Some methods in Cocoa specify that an object is returned by reference. There are several examples that use an NSError object that contains information about an error if one occurs, such as:

In these cases, the same rules apply as have already been described. When you invoke any of these methods, you do not create the NSError object so you do not own it—there is therefore no need to release it.

NSString *fileName = ... ;
NSError *error;
NSString *string = [[NSString alloc] initWithContentsOfFile:fileName
                        encoding:NSUTF8StringEncoding error:&error];
if (string == nil) {
    // deal with error ...
}
// ...
[string release];

If for any reason ownership of returned object does not follow the basic rules, this is stated explicitly in the documentation for the method (see for example, dataFromPropertyList:format:errorDescription:).

Delayed Release

The autorelease method, defined by NSObject, marks the receiver for later release. By autoreleasing an object—that is, by sending it an autorelease message—you declare that you don't want to own the object beyond the scope in which you sent autorelease. The scope is defined by the current autorelease pool—see “Autorelease Pools.”

The sprockets method mentioned above could be implemented in this way:

– (NSArray *)sprockets {
    NSArray *array;
 
    array = [[NSArray alloc] initWithObjects:mainSprocket,
                               auxiliarySprocket, nil];
    return [array autorelease];
}

When another method gets the array of Sprocket objects, that method can assume that the array will be disposed of when it is no longer needed, but can still be safely used anywhere within its scope (see “Validity of Shared Objects”). It can even return the array to its invoker, since the application object defines the bottom of the call stack for your code. The autorelease method thus allows every object to use other objects without worrying about disposing of them.

Just as it is an error to release an object after it is already been deallocated, it’s an error to send so many autorelease messages that the object would later be released after it had already been deallocated. You should send release or autorelease to an object only as many times as are allowed by its creation (one) plus the number of retain messages you have sent it (retain messages are described below).

Taking Ownership of Objects

There are times when you don’t want a received object to be disposed of; for example, you may need to cache the object in an instance variable. In this case, only you know when the object is no longer needed, so you need the power to ensure that the object is not disposed of while you are still using it. You do this with a retain message. A retain message is the opposite of a release of autorelease message. By retaining an object, (provided that everyone else plays by the same rules) you ensure that it won’t be deallocated until you are done with it. For example, if your object allows its main Sprocket to be set, you might want to retain that Sprocket like this:

– (void)setMainSprocket:(Sprocket *)newSprocket {
    [mainSprocket autorelease];
    mainSprocket = [newSprocket retain]; /* Claim the new Sprocket. */
    return;
}

Now, setMainSprocket: might get invoked with a Sprocket that the invoker intends to keep around, which means your object would be sharing the Sprocket with that other object. If that object changes the Sprocket, your object’s main Sprocket changes. You might want that, but if your Thingamajig needs to have its own Sprocket the method should make a private copy:

– (void)setMainSprocket:(Sprocket *)newSprocket {
    [mainSprocket autorelease];
    mainSprocket = [newSprocket copy]; /* Get a private copy. */
    return;
}

Note that both of these methods autorelease the original main sprocket, so they don’t need to check that the original main sprocket and the new one are the same. If they simply released the original when it was the same as the new one, that sprocket would be released and possibly deallocated, causing an error as soon as it was retained or copied. The following code solves that problem:

– (void)setMainSprocket:(Sprocket *)newSprocket {
    if (mainSprocket != newSprocket) {
        [mainSprocket release];
        mainSprocket = [newSprocket retain];
    }
}

Validity of Shared Objects

Cocoa’s ownership policy specifies that received objects should remain valid throughout the scope of the calling method. It should also be possible to return a received object from the current scope without fear of it being released. It should not matter to your application that the getter method of an object returns a cached instance variable or a computed value. What matters is that the object remains valid for the time you need it.

There are exceptions to this rule. For example, collection classes do not attempt to extend the lifetime of objects placed inside them. Removing an object from a mutable array could invalidate any copies of the object previously acquired, as in the following example:

value = [array objectAtIndex:n];
[array removeObjectAtIndex:n];
// value could now be invalid.

Another problem situation is when an object is deallocated after a call to one of its getter methods:

sprocket = [thingamajig mainSprocket];
[thingamajig release];
// sprocket could now be invalid.

To protect against situations like this, you could retain sprocket upon receiving it and release it when you have finished with it. Because it may not always be obvious when a caller should retain an object in this manner, the objects themselves should strive to return results that are valid in the current calling scope. In many cases, understanding how accessor methods are implemented, and implementing accessor methods appropriately, will resolve any confusion—see “Accessor Methods.”

Retain Cycles

In some situations, two objects may have cyclical references; that is, each object contains an instance variable that refers to the other object. For example, consider a text program with the object relationships shown in Figure 1. The Document object creates a Page object for each page in the document. Each Page object has an instance variable that keeps track of which document it is in. If the Document object retained the Page object and the Page object retained the Document object, neither object would ever be released. The Document’s reference count cannot become 0 until the Page object is released, and the Page object won’t be released until the Document object is deallocated.


Figure 1  An illustration of retain cycles

An illustration of retain cycles

The solution to the problem of retain cycles is that the “parent” object should retain its “children,” but that the children should not retain their parents. So, in Figure 1 the document object retains its page objects but the page object does not retain the document object. The child’s reference to its parent is an example of a weak reference, which is described more fully in “Weak References to Objects.”

Weak References to Objects

Retaining an object creates a “strong” reference to that object. An object cannot be deallocated until all of its strong references are released. An object’s lifetime is thereby determined by the owners of its strong references. In some cases, this behavior may not be desired. You may want to have a reference to an object without preventing the object from deallocating itself. For these cases, you can obtain a “weak” reference. A weak reference is created by storing a pointer to an object without retaining the object.

Weak references are essential in cases where a circular reference would otherwise be set up. For example, if Object A and Object B communicate with each other, each needs a reference to the other. If each retains the other, neither object ever gets deallocated until the connection is broken, but the connection is not broken until one of the objects is deallocated. Catch-22. To break the circle, one object takes a subordinate role and obtains a weak reference to the other. As a concrete example, in a view hierarchy, a parent view owns, and hence retains, its child views, but a child view does not own its parent; the child still needs to know who its parent is, so it keeps a weak reference to its parent.

Additional cases of weak references in Cocoa include, but are not restricted to, table data sources, outline view items, notification observers, and miscellaneous targets and delegates.

Important: In Cocoa, references to table data sources, outline view items, notification observers, and delegates are all considered weak (for example, an NSTableView object does not retain its data source and the NSApplication object does not retain its delegate). The documentation only describes exceptions to this convention.

You need to be careful about sending messages to objects for which you only hold a weak reference. If you send a message to an object after it has been deallocated, your application will crash. You must have well-defined conditions for when the object is valid. In most cases, the weak-referenced object is aware of the other object’s weak reference to it, as is the case for circular references, and is responsible for notifying the other object when it deallocates. For example, when you register an object with a notification center, the notification center stores a weak reference to the object and sends messages to it when the appropriate notifications are posted. When the object is deallocated, you need to unregister it with the notification center to prevent the notification center from sending any further messages to the object, which no longer exists. Likewise, when a delegate object is deallocated, you need to remove the delegate link by sending a setDelegate: message with a nil argument to the other object. These messages are normally sent from the object’s dealloc method.

Retain Count

Typically there should be no reason to explicitly ask an object what its retain count is (see retainCount). The result is often misleading, as you may be unaware of what framework objects have retained an object in which you are interested. In debugging memory management issues, you should be concerned only with ensuring that your code adheres to the ownership rules.

Deallocating an Object

Cocoa implements its ownership policy through a mechanism called “reference counting” or “retain counting.” When you create an object, it has a retain count of 1. When you send an object a retain message, its retain count is increased by 1. When you send an object a release message, its retain count is decreased by 1 (autorelease causes the retain count to be decremented in the future).

When its retain count drops to 0, an object’s memory is reclaimed—in Cocoa terminology it is “freed” or “deallocated.” When an object is deallocated, its dealloc method is invoked automatically. The role of the dealloc method is to free the object's own memory, and dispose of any resources it holds, including its object instance variables.

If your class has object instance variables, you must implement a dealloc method that releases them, and then invokes super's implementation. For example, if the Thingamajig class had name and sprockets instance variables, you would implement its dealloc method as follows:

- (void)dealloc
{
    [sprockets release];
    [name release];
    [super dealloc];
}

You should never invoke another object’s dealloc method directly.

Important: When an application terminates, objects may not be sent a dealloc message since the process’s memory is automatically cleared on exit—it is more efficient simply to allow the operating system to clean up resources than to invoke all the memory management methods. This has implications for how you implement a dealloc method—see “Resource Management.”

Resource Management

You should typically not manage scarce resources such as file descriptors, network connections, and buffers/caches in a dealloc method. In particular, you should not design classes such that you are assuming that dealloc will be invoked when you think it will be invoked. Invocation of dealloc might be delayed or sidestepped, either because of a bug or because of application tear-down.

Instead, if you have a class whose instances manage scarce resources, you should design your application such that you know when you no longer need the resources and can then tell the instance to “clean up” at that point. You would typically then release the instance and dealloc would follow, but you will not suffer additional problems if it does not.

Some of the problems that arise if you try to piggy-back resource management on top of dealloc include:

  1. Order dependencies on object graph tear-down.

    The object graph tear-down mechanism is inherently non-ordered. Although you might typically expect—and get—a particular order, you are introducing fragility. If an object falls in an autorelease pool unexpectedly, the tear-down order may change, which may lead to unexpected results.

  2. Non-reclamation of scarce resources.

    Memory leaks are of course bugs that should be fixed, but they are generally not immediately fatal. If scarce resources are not released when you expect them to be released, however, this can lead to much more serious problems. If your application runs out of file descriptors, for example, the user may not be able to save data.

  3. Clean-up logic being executed on the wrong thread.

    If an object falls into an autorelease pool at an unexpected time, it will be deallocated on whatever thread’s pool it happens to be in. This can easily be fatal for certain kinds of resource that should only ever be touched from one thread.

Summary

Now that the concepts behind the Cocoa’s object ownership policy have been introduced, they can be expressed as a short list of rules—see “Memory Management Rules.”



< Previous PageNext Page > Hide TOC


© 2009 Apple Inc. All Rights Reserved. (Last updated: 2009-05-06)


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.