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?
rptwsthi
  • 10,094
  • 10
  • 68
  • 109
Debajit
  • 46,327
  • 33
  • 91
  • 100

10 Answers10

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

Soner Gönül
  • 97,193
  • 102
  • 206
  • 364
Chris Hanson
  • 54,380
  • 8
  • 73
  • 102
  • 61
    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 Apr 27 '10 at 00:08
  • 63
    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 Apr 28 '10 at 00:06
  • 10
    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. – philsquared May 27 '10 at 10:38
  • 5
    @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 May 27 '10 at 12:18
  • 2
    @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. – philsquared May 27 '10 at 12:27
  • 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. – philsquared May 27 '10 at 13:19
  • @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 May 27 '10 at 13:50
  • 7
    @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. – philsquared May 27 '10 at 14:37
  • 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. – Rpranata Apr 15 '11 at 03:53
  • i tried with the statements above and found the value same as before.. no change in console message!! Had anyone checked?? – Tarun Dec 02 '13 at 09:24
  • @Tarun did you use retain or copy? – Unheilig Jan 06 '14 at 09:48
  • @Unheilig i checked with both copy & retain to see what it does. – Tarun Jan 07 '14 at 06:49
  • @Tarun and how was the result with both? Agreeing? – Unheilig Jan 07 '14 at 06:54
  • @Unheilig As mentioned it was same in both cases i.e chris. – Tarun Jan 07 '14 at 06:55
  • @RobNapier - Your link is dead. – uplearned.com May 24 '20 at 11:05
  • 1
    @uplearnedu.com Thanks; the updated link is https://robnapier.net/implementing-nscopying – Rob Napier May 24 '20 at 12:55
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).

Frank Krueger
  • 69,552
  • 46
  • 163
  • 208
  • 1
    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. – philsquared May 27 '10 at 14:38
  • 25
    +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`. – matm May 12 '11 at 14:33
  • What's the difference between this answer and the down voted one by @GBY? – Gary Lyn May 12 '13 at 10:21
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.
TJez
  • 1,969
  • 2
  • 19
  • 24
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.

Johannes Fahrenkrug
  • 42,912
  • 19
  • 126
  • 165
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.

Monolo
  • 18,205
  • 17
  • 69
  • 103
Divya Arora
  • 141
  • 1
  • 2
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.

Clarkeye
  • 889
  • 8
  • 9
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
len
  • 139
  • 1
  • 7
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;
}
onmyway133
  • 45,645
  • 31
  • 257
  • 263
-1

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

jack
  • 731
  • 7
  • 8
-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.

Echilon
  • 10,064
  • 33
  • 131
  • 217
GBY
  • 1,090
  • 1
  • 10
  • 13
  • 1
    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. – gnasher729 Mar 21 '14 at 12:06
  • 1
    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 change – Bryan P May 15 '14 at 05:11