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...
When does -copy return an immutable object (as expected) and when does it return a mutable object?
How do I achieve the intended effect of getting a "frozen" copy of a mutable object that refuses to be "frozen"?
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.
1) When does -copy return an immutable object (as expected) and when does it return a mutable object?
you should always treat it as the immutable variant. the mutable interface of the returned type should not be used. apart from optimizations, the answer should not matter and should be considered an implementation detail unless documented.
the obvious case: for a number of reasons, objc class clusters and class designs can be complex. returning a mutable copy could simply be for convenience.
2) How do I achieve the intended effect of getting a "frozen" copy of a mutable object that refuses to be "frozen"?
using the copy constructor of the immutable class is a good way (similar to St3fan's answer). like copy, it's not a guarantee.
the only reason i can think of as to why you would want to enforce this behaviour is for performance or to enforce a restricted interface (unless it's academic). if you want performance or a restricted interface, then you can simply encapsulate an instance of the type which copies on creation and exposes only the immutable interface. then you implement copy via retain (if that's your intent).
alternatively, you can write your own subclass and implement your own variant of copy.
final resort: many of the cocoa mutable/immutable classes are purely interface - you could write your own subclass if you need to ensure a particular behaviour -- but that's quite unusual.
perhaps a better description of why this should be enforced would be good - the existing implementations work just fine for the vast majority of developers/uses.
Bear in mind that there is not one copy implementation -- each class implements its own. And, as we all know, the implementation of the Objective C runtime is a little "loosey goosey" in spots. So I think we can say that mostly copy returns an immutable version, but some exceptions exist.
(BTW, what does this do:
NSArray *arr = [[NSMutable array] init];
?)
The best way to turn an object into an mutable one is to use the mutable 'constructor'. Like for example:
NSArray* array = ...;
NSMutableArray* mutableArray = [NSMutableArray arrayWithArray: array];
Copy is used to make a copy of an object. Not to change it's mutability.
Related
NSDictionary keys are id<NSCopying> but the value for a set is just id, and the docs indicate their values are retained. According to the Set Fundamentals of the Collection Programming Topics docs:
You can, however, modify individual objects themselves (if they support modification).
If you modify an object, this could affect the hashvalue of the object, which would affect lookups. I assumed that an NSSet is a fast lookup?
Here's an example that shows how things break if you mutate objects:
NSMutableString *str = [NSMutableString stringWithString: #"AWESOME"];
NSCountedSet *countedSet = [[NSCountedSet alloc] init];
[countedSet addObject: str];
[countedSet addObject: str];
NSLog(#"%#", #([countedSet countForObject: #"AWESOME"]));
[str appendString: #" NOT AWESOME"];
NSLog(#"%#", #([countedSet countForObject: #"AWESOME NOT AWESOME"]));
NSLog(#"%#", #([countedSet countForObject: #"AWESOME"]));
NSLog(#"%#", #([countedSet countForObject: str]));
for(NSString *s in countedSet) {
NSLog(#"%# - %#", str, #([countedSet countForObject: s]));
}
NSSet *set = [NSSet setWithArray: #[ str ]];
NSLog(#"Set Contains string, %#", #([set containsObject: str]));
[str appendString: #"asdf"];
NSLog(#"Set Contains string, %#", #([set containsObject: str]));
NSLog(#"%#", set);
And output with my interpretation:
[64844:303] 2 // Count is 2
[64844:303] 0 // Count should be 2 - if it looks for the literal string
[64844:303] 0 // Count should be 0, but can't find original object either
[64844:303] 0 // Count should be 2 - asking for actual object that's in there
[64844:303] AWESOME NOT AWESOME - 0 // Should be 2 - asking for actual object that it just retrieved
[64844:303] Set Contains string, 1 // Correct, pre-mutation
[64844:303] Set Contains string, 0 // Should be true, object is in there
[65070:303] {(
"AWESOME NOT AWESOMEasdf" // see? It's in there
)}
My take:
The set likely buckets based on hash value, when the hash is changed out behind the set, it doesn't know what to do and lookups are broken. The documentation is lacking in this area.
My question restated:
Docs say you can mutate objects, which is not intuitive.
Mutating objects breaks sets.
WTF?
That line from the docs is confusing. However, note that three paragraphs down it goes on to say:
If mutable objects are stored in a set, either the hash method of the
objects shouldn’t depend on the internal state of the mutable objects
or the mutable objects shouldn’t be modified while they’re in the set.
For example, a mutable dictionary can be put in a set, but you must
not change it while it is in there. (Note that it can be difficult to
know whether or not a given object is in a collection).
What your code is demonstrating is a known property of the hash-based collection classes. It can affect dictionaries, too, if a key object is implemented such that copying returns the original, which is inherently mutable.
There's no real way to test if an object is mutable. So, it can't force immutability.
Also, as alluded to in the quote above, it's possible to make a mutable class whose hash and equality are not affected by mutations.
Finally, it would too severely limit the utility of those collection classes if they could only be used with copyable classes and made copies of the elements (like dictionaries make copies of their keys). The collections are used to represent relationships, among other things, and it wouldn't do if you tried to establish a relationship between objects but instead established a relationship to a separate copy.
Since the only reliable way of ensuring an object's immutability in Objective-C is to make a copy, Cocoa designers had two choices:
Make NSSet copy the objects - That would be safe, bit it would severely restrict the use of NSSet due to increased memory usage.
Use retained objects - That would keep memory usage to a bare minimum, but it would give the users a way to shoot themselves in a foot by mutating an object inside NSSet.
Designers picked the second approach over the first one, because it fixes a danger that could be avoided by proper coding technique. In contrast, selecting the first approach would be "binding" on everybody, in the sense that inserting a new object would always make a copy.
Currently, users have a choice of inserting copies of objects that they create manually, thus emulating the first approach. However, an implementation that forces a copy cannot emulate an implementation that retains objects, making it a less flexible choice.
NSArray *array = [dictionary objectForKey:#"field"];
and
NSArray *array = [[NSArray alloc] initWithArray:[dictionary objectForKey:#"field"]];
I see both kind of approaches very frequently in objective C code.
When tried to understand, I found both of them used in similar situation too, which makes contradiction. I am not clear on when I should use 1st approach and when 2nd one?
Any idea?
Detailed explanation and useful references are moms welcome.
First off, those two examples are doing slightly different things. One is retrieving something from an existing dictionary and one is creating a new array by retrieving something from an existing dictionary (the value of that key is an array).
But, if you're asking the difference between getting objects by alloc vs. convenience methods. ([NSString alloc] init vs [NSString stringWith ...), by convention, you own anything that you call alloc, new copy or mutableCopy on. Anything that you call that is not those, is autoreleased.
See the memory guide here. Specifically, look at the rules.
Getting an autoreleased object means it will go away at some point in the near future. If you don't need to hold onto outside the scope of that function, then you can call autorelease on it or use one of the convenience methods that's not alloc, etc...
For example:
// my object doesn't need that formatted string - create the autoreleased version of it.
- (NSString) description {
return [NSString stringWithFormat:#"%# : %d", _title, _id];
}
// my object stuffed it away in an iVar - I need the retained version of it. release in dealloc
- (void) prepare {
_myVal = [[NSString alloc] initWithFormat:"string I need for %d", _id];
}
In the first example, I created a convenience methods for others to call, my class doesn't need that object beyond the scope of that method so I create the autoreleased version of it and return it. If the caller needs it beyond the scope of his calling method, he can retain it. If not he can use it and let it go away. Very little code.
In the second example, I'm formatting a string and assigning it to an iVar variable that I need to hold onto for the lifetime of my class so I call alloc which will retain it. I own it and releasing it eventually. Now, I could have used the first version here and just called retain on it as well.
You have a fundamental misunderstanding of allocations versus instance methods.
The first example, NSDictionary's -objectForKey method, returns id, not an instance of NSDictionary, therefore it does not allocate or initialize the variable.
The second, however is the classic retain part of the retain-release cycle.
The two methods are fundamentally equal (if we are to assume that array is alloc'd but empty in the first, and nil in the second), and both get ownership of the array object. I would go with the second, as it guarantees a reference, and it's shorter.
What I think you're confusing this with are new and convenience methods. Convenience methods (like NSNumber's +numberWithInt:, NSString's +stringWithFormat:, and NSMutableArray's +array), return an autorelease instance of the class (usually). New takes the place of alloc and init in just one word.
I hardly ever see the second one used and I wonder why?
Neither would it break support for situations where an NSArray is expected (as it's a subclass).
Nor would it break encapsulation by revealing mutable internals.
Under the precondition that it's never a mutable ivar that's returned, (which should be common sense anyway)
I can right now only think of advantages of using the second.
It actually is mutable. And muting is safe here, so why prevent it?
No need to call [[[foo fooBar] mutableCopy] autorelease], which needlessly allocates additional memory and needlessly wastes time.
Here are the method variations:
- (NSArray *)fooBar {
NSMutableArray *fooArray = [NSMutableArray array];
//populate fooArray
return fooArray;
}
- (NSMutableArray *)fooBar {
NSMutableArray *fooArray = [NSMutableArray array];
//populate fooArray
return fooArray;
}
I'm asking as my project has a bunch of methods with the same pattern.
And in most of the times the returned array will be modified afterwards (merged, edited, etc).
So I think it should be totally fine to return NSMutableArrays, yet nobody seems to be doing it.
NSMutableArray, NSMutableSet, NSMutableDictionary… it's basically the same deal.
For an explanation of using mutable versus immutable, check out Apple's documentation on Object Mutability.
In general, it is best to return an immutable version, unless it is specifically your intent that the object returned always be an immutable object available for any client to change. You should create your interfaces based on the intent of the interface, not off the current implementation. It is possible that requirements will change and you will need to change the implementation of fooBar such that it does return an instance variable. By returning mutable arrays you ensure that you encapsulate not only your instance variables, but your current implementation.
So, you may have a valid place to return a mutable array (I don't know), but you see most code passing immutable arrays because it fully encapsulates their variables and their implementations.
I suppose the first variation was preferred because polymorphism was preferred.
In either case, both methods return an instance of NSMutableArray, the only difference being that the first one hides that fact from the caller. In other words, the first variation is not safer than the second. It's essentially using polymorphism to tell the caller that any type of NSArray might be returned. If you need that kind of flexibility in your code, it definitely has it's advantages. (e.g., if one day, for whatever reason, you need to return a custom NSArray subclass, your code won't break at that level).
However, you seem to prefer communicating intent to the caller - i.e. that you actually return mutable arrays - which is also OK. To make everyone happy (if there is such thing anyways...), I suggest renaming the 2nd method to:
- (NSMutableArray *)mutableFooBar {
NSMutableArray *fooArray = [NSMutableArray array];
//populate fooArray
return fooArray;
}
As a side note, I think that the following is a slightly more efficient way to convert an existing immutable array into a mutable one:
NSMutableArray *mutableArray = [NSMutableArray arrayWithArray:fooArray];
(correct me if I'm wrong on that assumption).
I hope this answers your question...
Having a method return a mutable instance like that looks suspicious.
As the caller you have to question the original method signature and wonder if it really is safe to mutate the returned value. After all the class may inadvertently be returning a pointer to internal state.
If profiling reveals that this copy is indeed expensive, I usually change the method signature to make it obvious that the mutability is intended. Perhaps with something like:
- (void)populateFooBars:(NSMutableArray *)array;
That way it is clear that the mutability of the result is intentional.
In the following function which one is the best practice?
To send an autoreleased object, and make the caller retain it?
or send an allocated object, and make the caller release it?
- (NSString*) convertDataToString :(NSData*)myData
{
//just an example, method might not exist
NSString *str = [[NSString alloc] initWithData:myData];
return str;
return [str autoRelease];
}
Following up on #Chuck's comment, -convertDataToString must not return an object that the caller must release. That would violate the Three Magic Words. If you do not have "copy," "alloc," or "new" in your name, the caller cannot be expected to release the object. If you have "copy" in your name or start with "new" or "alloc," then the caller must release the object.
Objective-C relies heavily on consistent naming and the names mean things. If you learn the naming, then you won't have any problems.
The memory management rules say your first example is — and this is a direct quote — wrong. It's not even a matter of preference, as some answers here seem to indicate. The caller does not normally own the object you return, so it should be autoreleased.
The specific example from the rules says this:
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.
You'd want to return an autoreleased object most of the time.
Unless your method name contains one of the following words [alloc, new, copy], you should return an autoreleased object.
If you create, alloc, or copy an object, you are responsible for releasing it. Based on this, you should return an autoreleased object.
I prefer to return the autorelease. It means that you aren't hunting around trying to find where memory is being freed. Keeping memory allocation and deallocation together makes your life easier. After all, you're coding this, why make it harder on yourself.
Both are acceptable, but you should name your method accordingly : if the caller has the responsibility to deallocate it, you have to make this explicit by having your method name contain "create", "alloc" or "copy", else it should not.
More reading on this at http://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html%23//apple_ref/doc/uid/20000994-BAJHFBGH
It might be a bit more customary to return an autorelease object, but both are okay.
This produces an immutable string object:
NSString* myStringA = #"A"; //CORRECTED FROM: NSMutableString* myStringA = #"A";
This produces a mutable string object:
NSMutableString* myStringB = [NSMutableString stringWithString:#"B"];
But both objects are reported as the same kind of object, "NSCFString":
NSLog(#"myStringA is type: %#, myStringB is type: %#",
[myStringA class], [myStringB class]);
So what is distinguishing these objects internally, and how do I test for that, so that I can easily determine if a mystery string variable is immutable or mutable before doing something evil to it?
The docs include a fairly long explanation on why Apple doesn't want you to do this and why they explicitly do not support it in Receiving Mutable Objects. The summary is:
So don’t make a decision on object
mutability based on what introspection
tells you about an object. Treat
objects as mutable or not based on
what you are handed at the API
boundaries (that is, based on the
return type). If you need to
unambiguously mark an object as
mutable or immutable when you pass it
to clients, pass that information as a
flag along with the object.
I find their NSView example the easiest to understand, and it illustrates a basic Cocoa problem. You have an NSMutableArray called "elements" that you want to expose as an array, but don't want callers to mess with. You have several options:
Expose your NSMutableArray as an NSArray.
Always make a non-mutable copy when requested
Store elements as an NSArray and create a new array every time it mutates.
I've done all of these at various points. #1 is by far the simplest and fastest solution. It's also dangerous, since the array might mutate behind the caller's back. But Apple indicates it's what they do in some cases (note the warning for -subviews in NSView). I can confirm that while #2 and #3 are much safer, they can create major performance problems, which is probably why Apple has chosen not to use them on oft-accessed members like -subviews.
The upshot of all of this is that if you use #1, then introspection will mislead you. You have an NSMutableArray cast as an NSArray, and introspection will indicate that it's mutable (introspection has no way to know otherwise). But you must not mutate it. Only the compile-time type check can tell you that, and so it's the only thing you can trust.
The fix for this would be some kind of fast copy-on-write immutable version of a mutable data structure. That way #2 could possibly be done with decent performance. I can imagine changes to the NSArray cluster that would allow this, but it doesn't exist in Cocoa today (and could impact NSArray performance in the normal case, making it a non-starter). Even if we had it, there's probably too much code out there that relies on the current behavior to ever allow mutability introspection to be trusted.
There's no (documented) way to determine if a string is mutable at runtime or not.
You would expect one of the following would work, but none of them work:
[[s class] isKindOfClass:[NSMutableString class]]; // always returns false
[s isMemberOfClass:[NSMutableString class]]; // always returns false
[s respondsToSelector:#selector(appendString)]; // always returns true
More info here, although it doesn't help you with the problem:
http://www.cocoabuilder.com/archive/cocoa/111173-mutability.html
If you want to check for debugging purposes the following code should work. Copy on immutable object is itself, while it's a true copy for mutable types, that's what the code is based on. Note that since it's calling copy it's slow, but should be fine for debugging. If you'd like to check for any other reasons than debugging see Rob answer (and forget about it).
BOOL isMutable(id object)
{
id copy = [object copy];
BOOL copyIsADifferentObject = (copy != object);
[copy release];
return copyIsADifferentObject;
}
Disclaimer: of course there is no guarantee that copy is equivalent with retain for immutable types. You can be sure that if isMutable returns NO then it's not mutable so the function should be probably named canBeMutable. In the real world however, it's a pretty safe assumption that immutable types (NSString,NSArray) will implement this optimization. There is a lot of code out including basic things like NSDictionary that expects fast copy from immutable types.