Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When does -copy return a mutable object?

I read in Cocoa and Objective C: Up and Running that -copy will always return an immutable object and -mutableCopy will always return a mutable object:

It’s important to know that calling -copy on a mutable object returns an immutable version. If you want to copy a mutable object and maintain mutability in the new version, you must call -mutableCopy on the original. This is useful, though, because if you want to “freeze” a mutable object, you can just call -copy on it.

So I have something like this:

NSMutableURLRequest *req = [[NSMutableURLRequest alloc] init];
NSLog( @"%@", [req className] );               // NSMutableURLRequest
NSLog( @"%@", [[req copy] className] );        // NSMutableURLRequest
NSLog( @"%@", [[req mutableCopy] className] ); // NSMutableURLRequest

According to this previous answer:

You cannot depend on the result of copy to be mutable! Copying an NSMutableArray may return an NSMutableArray, since that's the original class, but copying any arbitrary NSArray instance would not.

This seems to be somewhat isolated to NSURLRequest, since NSArray acts as intended:

NSArray *arr = [[NSMutableArray alloc] init];
NSLog( @"%@", [arr className] );                 // __NSArrayM
NSLog( @"%@", [[arr copy] className] );          // __NSAraryI
NSLog( @"%@", [[array mutableCopy] className] ); // __NSArrayM

So...

  1. When does -copy return an immutable object (as expected) and when does it return a mutable object?
  2. How do I achieve the intended effect of getting a "frozen" copy of a mutable object that refuses to be "frozen"?
like image 659
rubergly Avatar asked Aug 27 '11 02:08

rubergly


1 Answers

I think you've uncovered a great rift between documentation and reality.

The NSCopying protocol documentation claims:

The copy returned is immutable if the consideration “immutable vs. mutable” applies to the receiving object; otherwise the exact nature of the copy is determined by the class.

But this is clearly wrong in some cases, as you've shown in your examples (and I've sent feedback to them about this via that documentation page).

But(#2) in my opinion, it doesn't actually matter and you shouldn't care.

The point of -copy is that it will return an object you can use with the guarantee that it will behave independently of the original. This means if you have a mutable object, -copy it, and change the original object, the copy will not see the effect. (In some cases, I think this means that -copy can be optimized to do nothing, because if the object is immutable it can't be changed in the first place. I may be wrong about this. (I'm now wondering what the implications are for dictionary keys because of this, but that's a separate topic...))

As you've seen, in some cases the new object may actually be of a mutable class (even if the documentation tells us it won't). But as long as you don't rely on it being mutable (why would you?), it doesn't matter.

What should you do? Always treat the result of -copy as immutable, simple as that.

like image 98
jtbandes Avatar answered Nov 01 '22 01:11

jtbandes