views:

10370

answers:

3

Let's say I have a class called SomeClass with a string property name:

@interface SomeClass : NSObject
{
    NSString* name;
}

@property (nonatomic, retain) NSString* name;

@end

I understand that name may be assigned a NSMutableString in which case this may lead to errant behavior.

  • For strings in general, is it always a good idea to use the "copy" attribute instead of "retain"?
  • Is a "copied" property in any way less efficient than such a "retain-ed" property?
A: 

I would argue that you should never use copy unless you really mean to make a copy of the object.

The reason why is it might break developer expectations. Other developers are probably expecting to be working with the same pointer when assigning objects around in code, and would expect a change to a value to reflect to all other classes pointing at that value. This is pretty subjective though depending on the developers background. (background in high level language vs low level language)

/edited out memory part since its superficial

seanalltogether
That's just not the appropriate guidance for Cocoa developers, sorry.
Chris Hanson
Why should developer intentions around a mutable string be different then mutable dictionaries and mutable arrays? You wouldn't use copy with a mutable data provider would you? If something is created as mutable why would you expect to seal it off as you're passing it around?
seanalltogether
I think the guidance for mutable dictionaries and mutable arrays should be the same: if your class assumes that it's going to be the only one changing the data (or if it has a controlled way of allowing third parties to change it) it should copy the data when it comes from an outside source.
Evan DiBiase
I should mention, too, that you need to take the client into consideration. If the client isn't expecting that you'll change their data, you should make a copy when they pass it to you. So, combine that with my previous comment, and I think you arrive at Chris's guidance.
Evan DiBiase
The guidance for mutable collections actually is the same: If it's a relationship, you don't pass around mutable collections, you pass immutable collections but implement the appropriate KVC collection accessors and use -mutable{Array,Set}ValueForKey: to manipulate the relationship.
Chris Hanson
+16  A: 

Copy should be used for NSString. If it's Mutable, then it gets copied. If it's not, then it just gets retained. Exactly the semantics that you want in an app (let the type do what's best).

Frank Krueger
I would still prefer the mutable and immutable forms to be discreet, but I hadn't realised before that copy may just be retain if the original string is immutable - which is most of the way there. Thanks.
Phil Nash
+50  A: 

For attributes whose type is an immutable value class that conforms to the NSCopying protocol, you almost always should specify copy in your @property declaration. Specifying retain is something you almost never want in such a situation.

Here's why you want to do that:

NSMutableString *someName = [NSMutableString stringWithString:@"Chris"];

Person *p = [[[Person alloc] init] autorelease];
p.name = someName;

[someName setString:@"Debajit"];

The current value of the Person.name property will be different depending on whether the property is declared retain or copy — it will be @"Debajit" if the property is marked retain, but @"Chris" if the property is marked copy.

Since in almost all cases you want to prevent mutating an object's attributes behind its back, you should mark the properties representing them copy. (And if you write the setter yourself instead of using @synthesize you should remember to actually use copy instead of retain in it.)

Chris Hanson
This answer may have caused some confusion (see http://robnapier.net/blog/implementing-nscopying-439#comment-1312). You are absolutely correct about NSString, but I believe you've made the point a bit too general. The reason NSString should be copied is that it has a common mutable subclass (NSMutableString). For classes that do not have a mutable subclass (particularly classes you write yourself), it is usually better to retain them rather than copy in order to avoid wasting time and memory.
Rob Napier
Your reasoning is incorrect. You should not be making the determination of whether to copy or retain based on time/memory, you should be determining that based on the desired semantics. That's why I specifically used the term "immutable value class" in my answer.It's also not a matter of whether a class has mutable subclasses, or is mutable itself.
Chris Hanson
It's a shame that Obj-C cannot enforce immutability by type. This is the same as C++'s lack of transitive const. Personally I work *as if* strings are always immutable. If I ever need to use a mutable string I'll never hand out an immutable reference if I may mutate it later. I'd consider anything different to be a code smell. As a result - in my code (that I work on alone) I use retain on all my strings. If I was working as part of a team I might look at things differently.
Phil Nash
@Phil Nash: I'd consider it a code smell to use different styles for projects you work on alone and projects you share with others. In every Language/Framework there are common rules or styles that developers agree upon. Disregarding them in private projects seems wrong. And for your rationale "In my code, I'm not returning mutable strings": That might work for your own strings, but you never know about strings you receive from frameworks.
Nikolai Ruhe
@Nikolai - are you saying that there are framework methods that return immutable strings but may change them? I know what you mean about it being a smell to use different styles depending on the project. My point is that I consider the whole "immutable strings may be mutable" thing to be a code smell that I can eliminate from my own projects.
Phil Nash
actually I just read up a bit on Frank Krueger's assertion and, while his comment seems to be a simplification, there is some substance to it.I'm not sure if I like it - but maybe using copy does give them best of both worlds.
Phil Nash
@Phil Nash: You are saying that you consider the fact that NSMutableString is derived from NSString a code smell. I wonder how you might be able to eliminate that from your projects.
Nikolai Ruhe
@Nikolai I just don't use `NSMutableString`, except as a transient "string builder" type (from which I immediately take an immutable copy). I would prefer them to be discreet types - but I will allow that the fact that copy is free to do a retain if the original string is not mutable mitigates most of my concern.
Phil Nash