333
votes

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?
10
Follow-up question: Should name be released in dealloc or not?Chetan
@chetan Yes it should!Jon

10 Answers

442
votes

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

121
votes

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

67
votes

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.
39
votes

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.

14
votes

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.

3
votes

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.

1
votes
@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
0
votes

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;
}
-1
votes

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.

-1
votes

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