(copy)
将调用正在设置的项目上的copy方法,因此它将取决于对象。让我们看一个NSString的例子,看看这是否有帮助?
两个类
@interface ObjectWithCopy : NSObject
@property (copy) NSString *aString;
@end
以及ObjectWithoutCopy
@interface ObjectWithoutCopy : NSObject
@property (strong) NSString *aString;
@end
这很容易,我们都知道NSString是不可变的,那么"copy"会做什么呢?记住NSMutableString是NSString的子类。因此我们可以将其传递给setter。这样做会发生什么呢?
NSMutableString *aMutableString = [[NSMutableString alloc] initWithString:@"Hello World"];
ObjectWithCopy *objectWithCopy = [[ObjectWithCopy alloc] init];
objectWithCopy.aString = aMutableString;
NSLog(@"ObjectWithCopy.aString = %@", objectWithCopy.aString);
[aMutableString appendString:@" and every other world"];
NSLog(@"ObjectWithCopy.aString after aMutableString was modified = %@", objectWithCopy.aString);
NSLog(@"Now what happens without copy?");
aMutableString = [[NSMutableString alloc] initWithString:@"Hello World"];
ObjectWithoutCopy *objectWithoutCopy = [[ObjectWithoutCopy alloc] init];
objectWithoutCopy.aString = aMutableString;
NSLog(@"ObjectWithoutCopy.aString = %@", objectWithoutCopy.aString);
[aMutableString appendString:@" and every other world"];
NSLog(@"ObjectWithoutCopy.aString after aMutableString was modified = %@", objectWithoutCopy.aString);
输出?
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
哇——我们的不可变字符串在
ObjectWithoutCopy
上被改变了?!?这是因为它实际上是一个NSMutableString,如果没有复制操作,我们只是指向传递进来的任何东西。因此,对于那个传递进来的对象所做的任何更改都将在类和函数变量中看到。这就是为什么经常建议在拥有NSString属性时使用
(copy)
,因为你不希望NSString发生变化。
复制使得赋值语句变成这样:
_aString = [passedInString copy];
。如一些人所指出的,能够执行任何符合
NSCopying协议的内容,但应创建独立副本以确保外部世界的更改不会影响您的类。因此,某人可以创建一个NSString子类(
你可能永远不应该这样做)覆盖复制操作无效,你仍然可以看到NSString在你的控制下发生变化,但一般来说,NSCopying是正确的,至少这是你真正能做到的最好的。
第1版:
正如@David在他的评论中提到的(也许是他真正的问题?),
[aMutableString copy]
实际上返回一个类型为
NSString
的对象。我完全同意这很令人困惑。如果我正在制作NSMutableString,我会让复制返回一个NSMutableString。实际上,我认为真正的WTF在于Apple创建了一个不可变类的可变子类。在C#/Java中,你没有一个可变的字符串,而是使用另一个称为StringBuilder的类。
那么如果这么令人困惑,为什么还有那么多人在NSString属性上使用copy?因为它很可能做到了你想要的。如果您的类具有NSString属性,则选择该属性是因为您不想担心字符串在你的控制下改变。为此,您真正需要的是字符串副本。如果它是NSMutableString,则您可能希望得到一个NSString复件,因为您不希望将那个可变的字符串赋出去,并且使其在您的控制下发生更改。如果NSMutableString上的复制返回NSMutableString,我不认为人们会使用复制属性,但会创建一个自定义setter来设置
_aString = [[NSString alloc] initWithString: inputString]
。这可能更清晰,如果你认为它是这样,那就是你应该做的。但是此时在NSString属性上使用复制已经成为传统,所以我可能会继续使用它。您可能同意或不同意,但如果您的问题是为什么人们使用复制,那就是我个人的原因。
编辑2:
@David问为什么不这样复制?“如果我复制一个字符串,那么我希望后续的调用能够改变它。”我认为,如果您真的想要副作用,您可能应该声明一个没有复制修饰符的NSMutableString (
您可能永远不应该使用copy和NSMutableString,而是创建自己的设置器)。
在我近15年的编程中,我真的想不出为什么我不想让设置字符串的人产生副作用,但我想让任何获得字符串副作用的普通人。那真的很奇怪。
此外,我不能为了我想要的情况(但只有设置器(您不能控制)告诉您)而将其广告化为NSString。我的意思是为什么?为什么你想让你的不可变属性有时可变,但只有当设置器(你不控制)告诉你要
,但你不希望来自设置对象的副作用。
并且你不告诉任何人,他们必须自己弄清楚?我的头疼。
我猜这就是苹果所做的事情。但这只是猜测。
此外,如果您可以使用不可变的数据类型,那么应该这样做。它减少了所有类型的复杂性,特别是多线程。
当然,如果你真的
非常想这样做,Objective-C (和大多数OOP语言)都可以让你这样做。这取决于您的程序员和团队。您还可以通过将手机撞击到头骨上来测试加速度计。我不认为苹果推荐任何一种方法。
@property (copy) Foo* f1;
,那么self.f1 = self.f2
的作用是(这里简化了):_f1 = [_f2 copy]
。copy
的工作方式取决于类 Foo。 - Martin Rcopy
方法返回的是一个不可变的 NSString,这是很不直观的。 - Aaron Brager-mutableCopy
吧;-) - jemmons