(copy)
will call the copy method on the item being set, so it will depend on the object. Let's see an example with NSString and see if that helps?
Two classes ObjectWithCopy
@interface ObjectWithCopy : NSObject
@property (copy) NSString *aString;
@end
and ObjectWithoutCopy
@interface ObjectWithoutCopy : NSObject
@property (strong) NSString *aString;
@end
Pretty easy so far, and we all know NSString is immutable, so what will "copy" do? Well remember NSMutableString is a subclass of NSString. So we could pass that in to the setter instead. What happens when we do this?
NSMutableString *aMutableString = [[NSMutableString alloc] initWithString:@"Hello World"];
ObjectWithCopy *objectWithCopy = [[ObjectWithCopy alloc] init];
objectWithCopy.aString = aMutableString;
NSLog(@"ObjectWithCopy.aString = %@", objectWithCopy.aString);
//Now we change aMutableString
[aMutableString appendString:@" and every other world"];
NSLog(@"ObjectWithCopy.aString after aMutableString was modified = %@", objectWithCopy.aString);
NSLog(@"Now what happens without copy?");
// Reset aMutableString
aMutableString = [[NSMutableString alloc] initWithString:@"Hello World"];
ObjectWithoutCopy *objectWithoutCopy = [[ObjectWithoutCopy alloc] init];
objectWithoutCopy.aString = aMutableString;
NSLog(@"ObjectWithoutCopy.aString = %@", objectWithoutCopy.aString);
//Now we change aMutableString and see what objectWithoutCopy.aString is?
[aMutableString appendString:@" and every other world"];
NSLog(@"ObjectWithoutCopy.aString after aMutableString was modified = %@", objectWithoutCopy.aString);
The output?
2014-02-02 10:40:04.247 TableViewCellWithAutoLayout[95954:a0b] ObjectWithCopy.aString = Hello World
2014-02-02 10:40:04.248 TableViewCellWithAutoLayout[95954:a0b] ObjectWithCopy.aString after aMutableString was modified = Hello World
2014-02-02 10:40:04.248 TableViewCellWithAutoLayout[95954:a0b] Now what happens without copy?
2014-02-02 10:40:04.248 TableViewCellWithAutoLayout[95954:a0b] ObjectWithoutCopy.aString = Hello World
2014-02-02 10:40:04.249 TableViewCellWithAutoLayout[95954:a0b] ObjectWithoutCopy.aString after aMutableString was modified = Hello World and every other world
Whoa - our immutable string was changed on ObjectWithoutCopy
!?!? That's because it really was a NSMutableString after all and without copy we are simply pointing to whatever was passed in. So any changes that occurred with that passed in object will be seen in both the class and the function variable. That is why it's so often suggested that when you have an NSString property you use (copy)
, you don't expect an NSString to change.
Copy makes the assign statement this _aString = [passedInString copy];
. As some have pointed out can do anything so long as it conforms to the NSCopying protocol, but should create an independent copy of the object to make sure change the outside world makes doesn't affect your class. So someone could create a subclass of NSString (which you probably should never do) that overrides copy to do nothing and you could still see your NSString change out from under you, but in general it's a pretty safe bet that NSCopying was done correctly. At least that's the best you can really do.
Edit 1:
As @David mentions in his comment (and is maybe his real question?), [aMutableString copy]
actually returns a object of type NSString
. I completely agree this is confusing. If I was making NSMutableString
I would have copy return a NSMutableString
. Actually I think the real WTF here is that Apple has created a mutable subclass of a non-mutable class. In C#/Java, you don't have a mutable string, instead you use another class called StringBuilder.
So if this is so confusing, why do so many people use copy
on NSString
properties? Because it most likely does what you actually want it to. If your class has an NSString
property, you picked that because you don't want to worry about the string changing out from under you. To do that you really want a copy of the string passed in. And if it's a NSMutableString
you probably want an NSString
copy because you don't want to give that mutable string out and have it changed out from under you. If copy on a NSMutableString
returned an NSMutableString
I don't think people would use the copy attribute, but create a custom setter that set _aString = [[NSString alloc] initWithString: inputString]
. This is probably clearer, and if you think it is that it is maybe what you should do. But at this point it's convention to use copy on NSString
properties, so I'll probably continue to use it. You may or may not agree with it, but your question is why do people use copy then this is why I do personally.
Edit 2:
@David asks why doesn't copy work this way? "If I copy a string then I want a subsequent call to be able to change it." I think if you really want the side effects you probably should be declaring an NSMutableString without the copy modifier (you probably should never use copy and NSMutableString, but create your own setter).
I really can't think of a reason in my nearly 15 years of programming why I would not want the person setting the string's side effect but I would want any old person who get's the string's side effect. That's really weird.
Further I can't for the life of me think of why I would want that situation but advertise it as an NSString instead. I mean why? Why would want for your immutable property to be sometimes mutable, but only when the setter (which you don't control) tells you too but you don't want the side effects from from object that was set with. AND you don't tell any one about it and they have to just figure it out? My head hurts.
My guess is this is why Apple did what it did. But it's only a guess.
Further, if you can get away with an immutable data type you should. It reduces all sorts of complexity especially with multithreading.
Of course Objective-C (and most OOP languages) let you do all of this if you really really want to. That's up to you programmer and your team. You can also test the accelerometer by bashing your phone against your skull. I don't think Apple recommends either approach though.