Mac OS X Reference Library Apple Developer
Search

Object Ownership and Disposal

It is good practice for a program to use as little memory as possible. Environments therefore define mechanisms and policies to allow you to manage your program’s memory. Although you can consider memory management of an Objective-C program in terms of the underlying implementation (described in “Behind the Scenes: Retain Counts”) is typically easier to think of it in terms of object ownership.

In an Objective-C program, objects are created and destroyed. To ensure that your application does not use more memory than necessary, objects should be destroyed when they are no longer needed. It is important of course, however, that objects are not destroyed if they are still needed. To support these requirements, Cocoa defines a mechanism, object ownership, by which you can specify when you need an object and when you have finished with it.

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

Object Ownership Policy

Any object may have one or more owner. As long as an object has at least one owner, it continues to exist. If an object has no owners, the runtime system destroys it automatically (see “Deallocating an Object”). To make sure it is clear when you own an object and when you do not, Cocoa sets the following policy:

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

Consider the following code fragment:

{
    Thingamajig *myThingamajig = [[Thingamajig alloc] init];
    // ...
    NSArray *sprockets = [myThingamajig sprockets];
    // ...
    [myThingamajig 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 when you’ve finished with it. When you obtain the sprockets array from the Thingamajig object, you do not “create” the array, so you do not send it a release message.

Behind the Scenes: Retain Counts

The ownership policy is implemented through reference counting—typically called “retain count” after the retain method. Each object has a retain count.

Important: 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.

Autorelease

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

You could implement the sprockets method mentioned above in this way:

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

You create the array using alloc; you therefore own the array and are responsible for later relinquishing ownership. This you do using 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, because the application object defines the bottom of the call stack for your code.

The autorelease method makes it easy for you to return an object from a method and still abide by the ownership policy. To illustrate, consider two incorrect implementations of the sprockets method:

  1. This is wrong. Following the ownership policy, it would result in a memory leak.

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

    The object’s reference to the new array object is limited to the sprockets method. After the method returns, the object loses its reference to the new object so cannot relinquish ownership. That in itself is not a problem. However, following the naming convention set out earlier, the caller is given no indication that it owns the returned object. The caller would therefore not relinquish ownership of the returned object, leading to a memory leak.

  2. This is also wrong. The object properly relinquishes ownership of the new array, however after the release message is sent the new array has no owner so it is immediately disposed of by the system. The method therefore returns an invalid (freed) object:

    – (NSArray *)sprockets {
     
        NSArray *array = [[NSArray alloc] initWithObjects:mainSprocket,
                                   auxiliarySprocket, nil];
        [array release];
        return array; // array is invalid here
    }

Finally, you could also implement the sprockets method correctly like this:

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

You don’t own the array returned from arrayWithObjects:, you are therefore not responsible for relinquishing ownership. You can, though, safely return it from the sprockets method.

Important: To understand this, it‚Äôs tempting to think of the arrayWithObjects: method itself being implemented using autorelease. Although correct in this case, it‚Äôs strictly an implementation detail. Just as you shouldn‚Äôt be concerned with an object‚Äôs actual retain count, you shouldn‚Äôt be concerned about whether an object returned to you is autoreleased or not. The only concern is, do you own it or not.

Validity of Shared Objects

Cocoa’s ownership policy specifies that received objects should typically 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 occasional exceptions to this rule, primarily falling into one of two categories.

  1. When an object is removed from one of the fundamental collection classes.

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

    When an object is removed from one of the fundamental collection classes, it is sent a release (rather than autorelease) message. If the collection was the only owner of the removed object, the removed object (heisenObject in the example ) is then immediately deallocated.

  2. When a “parent object” is deallocated.

    id parent = <#create a parent object#>;
    // ...
    heisenObject = [parent child] ;
    [parent release]; // Or, for example: self.parent = nil;
    // heisenObject could now be invalid.

    In some situations you retrieve an object from another object, then directly or indirectly release the parent object. If releasing the parent causes it to be deallocated, and the parent was the only owner of the child, then the child (heisenObject in the example) will be deallocated at the same time (assuming it is sent a release rather than an autorelease message in the parent’s dealloc method).

To protect against these situations, you retain heisenObject upon receiving it and release it when you have finished with it, for example:

heisenObject = [[array objectAtIndex:n] retain];
[array removeObjectAtIndex:n];
// use heisenObject.
[heisenObject release];

Accessor Methods

If your class has an instance variable that is an object, you must make sure that any object that is set as the value for the instance variable is not freed while you’re using it. You must therefore claim ownership of the object when it is set. You must also make sure you then relinquish ownership of any currently-held value.

For example, if your object allows its main Sprocket to be set, you might implement setMainSprocket: 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 (recall that copy also confers ownership):

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

Both of these implementations autorelease the original main sprocket. This avoids a problem that would arise if newSprocket and mainSprocket are the same object, and the Thingamajig is the only object that owns it: In this situation, when the sprocket is released, it is immediately deallocated, which causes an error as soon as it is retained or copied. The following implementation also solves that problem:

– (void)setMainSprocket:(Sprocket *)newSprocket {
    if (mainSprocket != newSprocket) {
        [mainSprocket release];
        mainSprocket = [newSprocket retain]; /* Or copy, if appropriate. */
    }
}

In all of these cases, though, it may look as if the final mainSprocket set for your object is leaked, because you don’t relinquish ownership of it. This is taken care of by the dealloc method, described in “Deallocating an Object.” Accessor methods and how you implement them are described in more detail in “Accessor Methods.”

Deallocating an Object

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 ownership of any object instance variables.

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

- (void)dealloc {
    [mainSprocket release];
    [auxiliarySprocket release];
    [super dealloc];
}

Important: You should never invoke another object‚Äôs dealloc method directly.

You should not tie management of system resources to object lifetimes; see “Resource Management.”

When an application terminates, objects may not be sent a dealloc message. Because 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.

Objects Returned by Reference

Some methods in Cocoa specify that an object is returned by reference (that is, ClassName ** or id *). 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 = <#Get a file name#>;
NSError *error = nil;
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:).

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.

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 resources that should only ever be touched from one thread.




Last updated: 2010-06-24

Did this document help you? Yes It's good, but... Not helpful...