NSString retain copy question - objective-c

I've seen a few posts on here about the issue of using retain or copy for strings. I still can't quite get my head around the difference or the importance.
In my case at the moment I have a class with a whole load of nsstrings to hold strings.
I want this class to only be instantiated once and I want its nsstring variables to change depending on the index clicked in a table view.
Would I be correct in saying that if I chose to use retain that my nsstrings would be overwritten each time I set their value on my tableview click and that if I chose copy I would somehow have 2 instances of each string....?
I'm sorry ..... I totally don't get it

This is a question about copying mutable objects vs. immutable ones. Since NSString objects are immutable (you cannot change their contents), they implement -copy like this:
- (id) copyWithZone: (NSZone *) zone
{
return [self retain];
}
If you think about it, there's no reason to duplicate an immutable object because that's a waste of memory. On the other hand, NSMutableString objects can see their contents change during their lifetime, so if you request a copy of an NSMutableString, you will get a real copy, a different object.
If your strings are not NSMutableStrings, it does not matter whether you retain or copy them. However, choosing the right method is important if you later refactor your code to use NSMutableStrings. A common logic should answer the following question for you: if I get an object whose contents may change outside, which value do I need? More often than not you will want to make a copy.

Related

Objective-C = operator vs stringWithString/arrayWithArray

I'm new to obj-c development but partly have background in C development. It might be a noob question but I couldn't get an exact answer in other places. What is the difference between these snippets for arrays and strings and possibly other types of objects:
NSArray *original = [NSArray arrayWithObjects:someObjects,nil];
//Case 1
NSArray *copy1 = original;
//Case 2
NSArray *copy2 = [NSArray arrayWithArray:original];
and for strings
NSString *original = #"aString";
//Case 1
NSString *copy1 = original;
//Case 2
NSString *copy2 = [NSString stringWithString:original];
If I make changes to copy1 and copy2 later will they be reflected on original objects? And does the same rules apply to other object types?
The second code snippet does for NSString what the first code snippet does for NSArray. There is no difference in the behavior, because both NSString and NSArray objects in Cocoa are immutable.
When you call [NSString stringWithString:original], Cocoa is smart enough not to create a new object: the reasoning behind this decision is that since original cannot be changed, there's nothing you could do to tell apart a copy from the original. Same goes for [NSArray arrayWithArray:original], because you get the same instance back.
Note: If someObjects is mutable, one could tell apart an array from its deep copy by modifying the object, and seeing if it changes in the other place. However, arrayWithArray: method makes a "shallow" copy, so you wouldn't be able to detect a difference even if the objects inside your array are mutable.
Your question is really about what objects pointers are pointing to. When you say make changes to copy1 and copy2 later, I guess you mean to the pointer contents, not to the object referenced by that pointer. This is a rather functional way to think, but it important non-the-less.
In your example, the array / string part doesn't matter, because you aren't doing anything with the objects, you are just doing things with the pointers to those objects.
original points to one object. copy1 points to the same object. copy2 points to a different object (but which, in this case, is a copy of the first object).
copy1 is not a copy, but another pointer to the same memory as original. copy2 is actually a copy, pointing at a different piece of memory.
If you modify copy1 (assuming it was mutable, which you example code is not), you are modifying original too, as they point at the same piece of memory.
If you modify copy2, original should be unchanged (generally speaking). In your array example, the objects in the array original and in the array copy2 are, I believe the same. So you in this case, you have two arrays, but they have in them the same objects.
NSArrays and NSStrings are immutable so you can't change them.
You can't add or remove objects from NSArray, but if you change some object in array, it will change in its copy because NSArray holds a pointer to it.

Does copy also transfer the retain count?

Suppose I have an object, for example an NSString, with retain count 5. When I call copy on it, I get a new copy of the object; does this new object have the retain count of its original object ?
It depends. copy is a convenience method for copyWithZone:,
and the "NSCopying Protocol Reference" states:
Your options for implementing this protocol are as follows:
Implement NSCopying using alloc and init... in classes that don’t
inherit copyWithZone:.
Implement NSCopying by invoking the
superclass’s copyWithZone: when NSCopying behavior is inherited. If
the superclass implementation might use the NSCopyObject function,
make explicit assignments to pointer instance variables for retained
objects.
Implement NSCopying by retaining the original instead of
creating a new copy when the class and its contents are immutable.
(I have modified the following two statements after all that feedback.)
For example, NSString is an immutable object, and copy just retains the object
and returns a pointer to the same object. Retaining the object might increase the
retain count, but not necessarily (as in the case of string literals.)
Copying an NSMutableString would probably create a new object and return that.
The new object would have its own retain count independent of the original object.
But you should not care about the difference. With manual reference counting,
copy returns an object that you own and have to release eventually.
With ARC, the compiler with automatically handle that.
copy returns an object that is a semantic [shallow] copy(1) of the object. What the copy method returns is an implementation detail; it may return the same object, it may return a different instance of the same class, or it might even return an instance of a different class.
Doesn't matter.
What matters is that the returned object, under manual retain/release, has a retain count of +1. Not 1, but +1. It might actually be 1, 42, 981, or -1. Doesn't matter.
All that matters is that you must balance that retain with a release or autorelease somewhere if you want to relinquish the object back to the system. Which may not actually cause it to be deallocated; that is an implementation detail of no concern (until optimization time, anyway).
(1) semantic [shallow] copy means that the object returned is an effective shallow copy. The state contained within the copied object (but not within objects contained within the object -- that is the shallow part) will not change when the original object changes state. For a mutable object, copy must actually create a new instance of some object -- most likely an immutable variant class -- that can contain the original state.
For an immutable object, the copy method might be simply implemented as return [self retain];. Or, in the case of static NSStrings (NSCFStrings), it might simply be return self; as retain/release/autorelease are no-ops on such strings.
No, a copied object will have a retain count of 1, just like a newly initialized object.
I highly recommend you read the Memory Management Guide if you wish to learn more.
If you're new to iOS development, the iOS App Programming Guide should be read first, and is a great use of your time.
I just noticed you didn't tag this as iOS specific, if you're coding for Mac, the Programming with Objective-C guide might be more appropriate for you.
To really get your head around this issue, don't think in terms of retain count, think in terms of pointer ownership (like ARC does).
If an object has a "retain count" of 5, that means five pieces of code somewhere are each holding a (strong) pointer to its memory address. If you copy that object, you get a pointer to the address of the new, copied object. The other five pieces of code are still pointing to the original object. There's only one piece of code pointing to the new object, so its "retain count" is one.
As noted in other answers, the Memory Management Guide definitely helps make this all clear.
Why did I put "retain count" in quotes? Because it's only useful as a general concept -- you shouldn't be using retainCount directly, or you'll hear from #bbum.
Objective-C plays some clever tricks when you ask it to copy objects, and as a result retain counts might not be what you think they should be.
Let's say you have an object pointer x with a retain count of n, and call the copy method which returns an object pointer y.
NSObject* x = ...;
NSObject* y = [x copy];
Then the rule is that if you release x n times, and release y once, all objects will be gone. Usually this is achieved by leaving x unchanged, and giving y a retain count of 1.
However, if x points to an immutable object, then Objective-C may decide that no copy needs to be done. The result is that y = x. Still, the rule above still applies: Releasing x n times and y once (even though they are the same object) will free all the objects involved. That's achieved by the copy method returning x with a +1 retain count.
Copy on mutable object like NSMutableArray will create a new copy and retain count will be 1 while copy immutable object like NSArray will point to same reference and increase the retain count by 1.

Use of an intermediary to initialize an object

I've always seen that we use an intermediary object, for example, creating an array to fill in another array:
characters = [[NSArray alloc] initWithObjects:#"Antony", #"Artemidorus", #"Brutus", nil];
play.characters = characters;
[characters release];
with characters being an NSArray in the object play.
I saw the same thing with a #property and its self: we did not add the new items directly into this property, just as we don't directly fill in characters in the example above. Is this only about "style"?
This is not a matter of style.
play.characters is a property, and that can "contain" an existing array or nil, but even if it "contains" an existing array, you can't change the contents of an NSArray, so you'll have to create a new one and assign that to the property.
Assigning to a property will, if all was declared well, cause its setter method to run (which could be created by the compiler, if you used #synthesize, or written by you, in code) and that will take care of removing any existing array, assigning the new one and retaining it.
There is actually only one array in play in that little piece of code.
It is not the array that is intermediate, but the variable holding a pointer to it - in this case the variable characters.
This is what happens:
The expression
[[NSArray alloc] initWithObjects:#"Antony", #"Artemidorus", #"Brutus", nil]
allocates an object and initialises it with three NSStrings (which are themselves objects, but let's leave that out for a moment). The initialisation also includes an increment of the retain count, so it is one from the get-go.
This newly created object lives at a given position in memory, say 0100H. This position is then stored in the variable characters. In C terms we say that characters is a pointer to the object.
Then the property #"characters" of the object play is set to point to the same position in memory as the local variable characters. There are therefore now two variables (of which one is also a property) that point to the same object, or, if you prefer, to the same position in memory. If the property is of type retain, this will automatically increment the retain count of the object, so it is now 2.
With the release message in the last line, the object decrements its retain count by one, so at the end of this code snippet, the object is pointed to by the play.characters property, and it has a retain count of one.
To be really clean, this code should probably set the local variable to nil, to avoid confusion between variables holding pointers to the object and the retain count.
All this was meant to show that there really is only one array in play here, but two variables that point to it. So there are not as many computer resources being wasted as it might seem at a first glance.
If you wanted to do it all in a single line, you could write something like this:
play.characters = [[[NSArray alloc] initWithObjects:#"Antony", #"Artemidorus", #"Brutus", nil] autorelease];
but the exact working of this is less clear as it involves one of those mysterious autoreleases, i.e., a release that is handled automatically and postponed to some later stage.
This is a long description, but I hope it sheds some light on what is going on.

Returning mutable vs. returning immutable (non-member-)objects

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.

Cocoa: Testing to find if an NSString is immutable or mutable?

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.