tags:

views:

74

answers:

2

I have a number of functions similar to the following:

+ (NSArray *)arrayOfSomething
{
    NSMutableArray *array = [NSMutableArray array];

    // Add objects to the array

    return [[array copy] autorelease];
}

My question is about the last line of this method: is it better to return the mutable object and avoid a copy operation, or to return an immutable copy? Are there any good reasons to avoid returning a mutable object where one is not expected?

(I know that it is legal to return a NSMutableArray since it is a subclass of NSArray. My question is whether or not this is a good idea.)

+2  A: 

Short Answer: Don't do it

Long Answer: It depends. If the array is getting changed while being used by someone who expects it be static, you can cause some baffling errors that would be a pain to track down. It would be better to just do the copy/autorelease like you've done and only come back and revisit the return type of that method if it turns out that there is a significant performance hit.


In response to the comments, I think it's unlikely that returning a mutable array would cause any trouble, but, if it does cause trouble, it could be difficult to track down exactly what the issue is. If making a copy of the mutable array turns out to be a big performance hit, it will be very easy to determine what's causing the problem. You have a choice between two very unlikely issues, one that's easy to solve, one that's very difficult.

kubi
w.m
Somewhere down the line some code could test if the class was mutable and start modifying it. The would be problematic for other classes who expect an immutable array. It's not super likely, but it can happen.
Robot K
Also, some classes (especially framework classes) check to see if they're passed a mutable array and then copy it. Returning an immutable array would skip that step.
Robot K
Of course, if you're not writing a framework (or anything that is externally visible beyond your app/team) then it shouldn't be an issue.
Luke
It could still be an issue if you're passing your array to Apple's or 3rd party frameworks. Also, you have to consider that whoever has to maintain your code (you in six months?) may not know or remember (or may mis-remember) the implementation details and start making assumptions that aren't true.
Robot K
Agree totally. However, I am throwing it out there as an option depending on what the ultimate goal here is.
Luke
@kubi. Looks like there is a lot of anecdotal evidence also that working with mutable arrays mean performance hits. I think either way it's probably worth doing the conversion in the event that you're not passing this message all that often. If you are, maybe do the conversion outside.
Luke
@RobotK. There's no way to check for mutability of an NSArray subclass (without resorting to private APIs or other hackery), is there?
jlehr
Is there some reason that `if ([myArray isKindOfClass:[NSMutableArray class]]) { ...` wouldn't work?
Robot K
@Robot K: NSMutableArray and NSArray are part of a class cluster. You might get a different kind of object depending on the circumstances and sometimes, the difference is a just flag in the object itself that tells you whether it is mutable or not. It might work better to see if it responds to the mutating method you want to use.
JeremyP
@Robot K:: scratch my previous comment. You should **never** use introspection on a returned alleged immutable object to see if you can mutate it. See my answer below.
JeremyP
+2  A: 

This is a complex topic. I think it's best to refer you to Apple's guidelines on object mutability.

Apple has this to say on the subject of using introspection to determine a returned object's mutability:

To determine whether it can change a received object, the receiver must rely on the formal type of the return value. If it receives, for instance, an array object typed as immutable, it should not attempt to mutate it. It is not an acceptable programming practice to determine if an object is mutable based on its class membership

(my emphasis)

The article goes on to give several very good reasons why you should not use introspection on a returned object to determine if you can mutate it e.g.

You read a property list from a file. When the Foundation framework processes the list it notices that various subsets of the property list are identical, so it creates a set of objects that it shares among all those subsets. Afterwards you look at the created property list objects and decide to mutate one subset. Suddenly, and without being aware of it, you’ve changed the tree in multiple places.

and

You ask NSView for its subviews (subviews method) and it returns an object that is declared to be an NSArray but which could be an NSMutableArray internally. Then you pass that array to some other code that, through introspection, determines it to be mutable and changes it. By changing this array, the code is mutating NSView’s internal data structures.

Given the above, it is perfectly acceptable for you to return the mutable array in your example (provided of course, you never mutate it yourself after having returned it, because then you would be breaking the contract).

Having said that, almost nobody has read that section of the Cocoa Objects Guide, so defensive programming would call for you to make an immutable copy and return that unless performance profiling shows that it is a problem to do that.

JeremyP
Man, I've referred to that document many times and somehow always glanced past that section. I'm exactly the kind of programmer you want to defend against. (In my "defense", I've never actually tested the mutability of an object.)
Robot K
@Robot K: Join the club. As my first comment to you attached to kubi's answer shows, I would never have questioned the legitimacy of using introspection on returned immutable objects, if I hadn't decided to research the topic for this question. Like you though, I have never tried anything like that.
JeremyP