views:

164

answers:

3

I'm working on an iPhone project where I would like to retrieve an object from an NSMutableArray, remove the object from the array and then use it later. The code looks something like this:

NSMutableArray * array;
// fill the array
NSObject * obj = [array lastObject];
[array removeLastObject];
// do something with obj (in the same function)

array is the only entity with a retain on the object that is being accessed. Is this safe to do? I would assume that this would only work if lastObject autoreleased the object which is something that I can't figure out if it does.

+2  A: 
NSObject * obj = [array lastObject]; // Just returns an object, its retain count not changed
[array removeLastObject]; // object receives release message

So if your array is the only entity that retain your object then after removing it its retain count becomes zero and it should get deallocated. To work safely with that object you should retain it and release when it is not needed:

NSObject * obj = [[array lastObject] retain];
[array removeLastObject];
// do something with obj (in the same function)
[obj release];
Vladimir
+4  A: 

Why not call

[array removeLastObject]

at the end of your function? That's one fewer release/retain. It might make your code more readable/less cluttered.

For the record the Apple documentation:

Like NSArray, instances of NSMutableArray maintain strong references to their contents. If you do not use garbage collection, when you add an object to an array, the object receives a retain message. When an object is removed from a mutable array, it receives a release message. If there are no further references to the object, this means that the object is deallocated. If your program keeps a reference to such an object, the reference will become invalid unless you send the object a retain message before it’s removed from the array. For example, if anObject is not retained before it is removed from the array, the third statement below could result in a runtime error:

id anObject = [[anArray objectAtIndex:0] retain]; 
[anArray removeObjectAtIndex:0]; 
[anObject someMessage];
sylvanaar
To make the consequence of this clear: The NSArray and NSMutableArray classes have exceptions to the general rule that objects returned from methods whose names do not contain "new, "copy" or "alloc" return autoreleased objects. This means the code in the original question is unsafe.
harms
@harms doesn't that rule apply only to object creation?
sylvanaar
@harms: there is no general rule that objects returned from methods whose names do not contain "new, "copy" or "alloc" return autoreleased objects. The only rule is that you do not own such an object.
JeremyP
@harms Your statement is plain wrong.
Nikolai Ruhe
@sylvanaar No, the rule is general, but a consequence of it is that methods conferring ownership are typically creation methods, as well as `retain` of course.
harms
@JeremyP and @Nikolai Ruhe: I re-read the Cocoa memory management rules now, and I was right. See the "Validity of Sharred Objects" section. It states: "Cocoa’s ownership policy specifies that received objects should typically remain valid throughout the scope of the calling method." It goes on: "There are occasional exceptions to this rule [...] When an object is removed from one of the fundamental collection classes, it is sent a release (rather than autorelease) message."
harms
+2  A: 

It's worth mentioning the Cocoa Memory Management Rules here. If you are working in a non-GC environment e.g. iPhone, it's always worth mentioning them.

The fundamental rule state:

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.

Clearly you did not obtain obj with alloc, new... or something containing copy. So you do not own it.

In your code, the second of the corollaries is also relevant:

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 an object from which you received another object). That method may also safely return the object to its invoker

I have bolded the relevant part. You have modified the array from which you received the object, so it might go away. You either retain the object before removing it from the array or do the remove after you have finished with the object.

Actually, you might be safe even with the code in your question because NSMutableArray's implementation of of objectAtIndex: might do a retain followed by an autorelease on the return item. In a multithreaded environment that would be the only way to guarantee that the object remains valid for long enough to return it to the caller. However, I've not seen the code for NSMutableArray, so do not rely on it.

ETA: Just been looking at the thread programming guide and it appears that NSMutableAtrray does not do retain, autorelease.

JeremyP