NSString property: copy or retain?
Asked Answered
P

10

336

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?
Puttier answered 23/12, 2008 at 1:56 Comment(2)
Follow-up question: Should name be released in dealloc or not?Araxes
@chetan Yes it should!Grate
C
442

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.)

Connubial answered 23/12, 2008 at 2:23 Comment(16)
This answer may have caused some confusion (see 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.Kirovabad
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.Connubial
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.Entryway
@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.Footage
@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.Entryway
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.Entryway
@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.Footage
@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.Entryway
Yap, I think If I may, the decision whether you use copy or retain is completely depend on your semantics of your logic. (just like @Chris answer) If you really want your "assigned" (not pointer assignment) instance variable immutable, then use copy. BUT I think this is not a silver bullet, you are still valid to use retain.Durative
i tried with the statements above and found the value same as before.. no change in console message!! Had anyone checked??Minard
@Minard did you use retain or copy?Abreact
@Abreact i checked with both copy & retain to see what it does.Minard
@Minard and how was the result with both? Agreeing?Abreact
@Abreact As mentioned it was same in both cases i.e chris.Minard
@RobNapier - Your link is dead.Diner
@uplearnedu.com Thanks; the updated link is robnapier.net/implementing-nscopyingKirovabad
E
121

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).

Elevator answered 23/12, 2008 at 2:11 Comment(3)
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.Entryway
+1 for mentioning that NSString property declared as copy will get retain anyway (if it's immutable, of course). Other example I can think of is NSNumber.Extrasensory
What's the difference between this answer and the down voted one by @GBY?Deepset
S
68

For strings in general, is it always a good idea to use the copy attribute instead of retain?

Yes - in general always use the copy attribute.

This is because your NSString property can be passed an NSString instance or an NSMutableString instance, and therefore we can not really determine if the value being passed is an immutable or mutable object.

Is a "copied" property in any way less efficient than such a "retain-ed" property?

  • If your property is being passed an NSString instance, the answer is "No" - copying is not less efficient than retain.
    (It's not less efficient because the NSString is smart enough to not actually perform a copy.)

  • If your property is passed an NSMutableString instance then the answer is "Yes" - copying is less efficient than retain.
    (It's less efficient because an actual memory allocation and copy must occur, but this is probably a desirable thing.)

  • Generally speaking a "copied" property has the potential to be less efficient - however through the use of the NSCopying protocol, it's possible to implement a class which is "just as efficient" to copy as it is to retain. NSString instances are an example of this.

Generally (not just for NSString), when should I use "copy" instead of "retain"?

You should always use copy when you don't want the internal state of the property changing without warning. Even for immutable objects - properly written immutable objects will handle copy efficiently (see next section regarding immutability and NSCopying).

There may be performance reasons to retain objects, but it comes with a maintenance overhead - you must manage the possibility of the internal state changing outside your code. As they say - optimize last.

But, I wrote my class to be immutable - can't I just "retain" it?

No - use copy. If your class is really immutable then it's best practice to implement the NSCopying protocol to make your class return itself when copy is used. If you do this:

  • Other users of your class will gain the performance benefits when they use copy.
  • The copy annotation makes your own code more maintainable - the copy annotation indicates that you really don't need to worry about this object changing state elsewhere.
Scrawny answered 23/5, 2012 at 7:14 Comment(0)
F
40

I try to follow this simple rule:

  • Do I want to hold on to the value of the object at the point in time when I am assigning it to my property? Use copy.

  • Do I want to hold on to the object and I don't care what its internal values currently are or will be in the future? Use strong (retain).

To illustrate: Do I want to hold on to the name "Lisa Miller" (copy) or to I want to hold on to the person Lisa Miller (strong)? Her name might later change to "Lisa Smith", but she will still be the same person.

Fayina answered 3/12, 2013 at 15:20 Comment(0)
M
14

Through this example copy and retain can be explained like:

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

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

[someName setString:@"Debajit"];

if the property is of type copy then ,

a new copy will be created for the [Person name] string that will hold the contents of someName string. Now any operation on someName string will have no effect on [Person name].

[Person name] and someName strings will have different memory addresses.

But in case of retain,

both the [Person name] will hold the same memory address as of somename string, just the retain count of somename string will be incremented by 1.

So any change in somename string will be reflected in [Person name] string.

Meleager answered 20/9, 2012 at 8:17 Comment(0)
L
3

Surely putting 'copy' on a property declaration flies in the face of using an object-oriented environment where objects on the heap are passed by reference - one of the benefits you get here is that, when changing an object, all references to that object see the latest changes. A lot of languages supply 'ref' or similar keywords to allow value types (i.e. structures on the stack) to benefit from the same behaviour. Personally, I'd use copy sparingly, and if I felt that a property value should be protected from changes made to the object it was assigned from, I could call that object's copy method during the assignment, e.g.:

p.name = [someName copy];

Of course, when designing the object that contains that property, only you will know whether the design benefits from a pattern where assignments take copies - Cocoawithlove.com has the following to say:

"You should use a copy accessor when the setter parameter may be mutable but you can't have the internal state of a property changing without warning" - so the judgement as to whether you can stand the value to change unexpectedly is all your own. Imagine this scenario:

//person object has details of an individual you're assigning to a contact list.

Contact *contact = [[[Contact alloc] init] autorelease];
contact.name = person.name;

//person changes name
[[person name] setString:@"new name"];
//now both person.name and contact.name are in sync.

In this case, without using copy, our contact object takes the new value automatically; if we did use it, though, we'd have to manually make sure that changes were detected and synced. In this case, retain semantics might be desirable; in another, copy might be more appropriate.

Loosing answered 21/5, 2012 at 21:52 Comment(0)
V
1
@interface TTItem : NSObject    
@property (nonatomic, copy) NSString *name;
@end

{
    TTItem *item = [[TTItem alloc] init];    
    NSString *test1 = [NSString stringWithFormat:@"%d / %@", 1, @"Go go go"];  
    item.name = test1;  
    NSLog(@"-item.name: point = %p, content = %@; test1 = %p", item.name, item.name, test1);  
    test1 = [NSString stringWithFormat:@"%d / %@", 2, @"Back back back"];  
    NSLog(@"+item.name: point = %p, content = %@, test1 = %p", item.name, item.name, test1);
}

Log:  
    -item.name: point = 0x9a805a0, content = 1 / Go go go; test1 = 0x9a805a0  
    +item.name: point = 0x9a805a0, content = 1 / Go go go, test1 = 0x9a84660
Votaw answered 7/3, 2014 at 3:10 Comment(0)
K
0

You should use copy all the time to declare NSString property

@property (nonatomic, copy) NSString* name;

You should read these for more information on whether it returns immutable string (in case mutable string was passed) or returns a retained string (in case immutable string was passed)

NSCopying Protocol Reference

Implement NSCopying by retaining the original instead of creating a new copy when the class and its contents are immutable

Value Objects

So, for our immutable version, we can just do this:

- (id)copyWithZone:(NSZone *)zone
{
    return self;
}
Kr answered 29/5, 2014 at 18:33 Comment(0)
A
-1

Since name is a (immutable) NSString, copy or retain makes no difference if you set another NSString to name. In another word, copy behaves just like retain, increasing the reference count by one. I think that is an automatic optimization for immutable classes, since they are immutable and of no need to be cloned. But when a NSMutalbeString mstr is set to name, the content of mstr will be copied for the sake of correctness.

Avi answered 5/8, 2011 at 22:30 Comment(2)
You are confusing the declared type with the actual type. If you use a "retain" property and assign an NSMutableString, that NSMutableString will be retained, but can still be modified. If you use "copy", an immutable copy will be created when you assign an NSMutableString; from then on "copy" on the property will just retain, because the copy of the mutable string is itself immutable.Sclerotomy
You are missing some important facts here, if you use an object that came from a retained variable, when that variable gets modified, so does your object, if it came from a copied variable, your object will have the current value of the variable which will not changeDick
N
-1

If the string is very large then copy will affect performance and two copies of the large string will use more memory.

Nephew answered 17/1, 2015 at 17:11 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.