我假设copy属性的目的是让我可以将一个属性分配给另一个(比方说)并让底层的getter/setter方法正确处理所有事情.但是我已经回顾了很多文章(包括一些关于stackoverflow的文章),我不清楚仅仅使用适当的方法来明确地复制内容有什么好处.首先,我发现将可变字符串分配给另一个可变字符串仍然会留下一个不可变的字符串,那么有什么用呢?
这适用于其他类吗?换句话说,一般来说,如果我有
@property (copy) Foo* f1;
@property (copy) Foo* f2;
....
@property (copy) Foo* fn;
Run Code Online (Sandbox Code Playgroud)
我一般可以写
f1 = f2
f3 = f2
Run Code Online (Sandbox Code Playgroud)
并获得适当的副本,即完全独立的对象?
(copy)将在正在设置的项目上调用复制方法,因此它将取决于对象.让我们看一个NSString的例子,看看是否有帮助?
两节课 ObjectWithCopy
@interface ObjectWithCopy : NSObject
@property (copy) NSString *aString;
@end
Run Code Online (Sandbox Code Playgroud)
和 ObjectWithoutCopy
@interface ObjectWithoutCopy : NSObject
@property (strong) NSString *aString;
@end
Run Code Online (Sandbox Code Playgroud)
到目前为止很容易,我们都知道NSString是不可变的,那么"复制"会做什么?记得NSMutableString是NSString的子类.所以我们可以将其传递给setter.当我们这样做时会发生什么?
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);
Run Code Online (Sandbox Code Playgroud)
输出?
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
Run Code Online (Sandbox Code Playgroud)
哇 - 我们的不可变字符串被改变了ObjectWithoutCopy!?!?那是因为它毕竟是一个NSMutableString,没有副本我们只是指向传入的内容.所以在对象中传递的任何更改都将在类和函数变量中看到.这就是为什么经常建议当你使用NSString属性时(copy),你不希望改变NSString.
复制使这个assign语句_aString = [passedInString copy];.正如有些人指出的那样只要它符合NSCopying协议就可以做任何事情,但是应该创建一个对象的独立副本,以确保外部世界做出的改变不会影响你的类.所以有人可以创建一个NSString的子类(你可能永远不应该这样做)覆盖副本什么都不做,你仍然可以看到你的NSString从你的下面改变了,但一般来说,NSCopying正确完成是一个非常安全的赌注.至少那是你真正做到的最好的.
编辑1:正如@David在他的评论中提到的(也许是他真正的问题?),[aMutableString copy]实际上会返回一个类型的对象NSString.我完全同意这令人困惑.如果我正在制作NSMutableString我会复制返回一个NSMutableString.实际上我认为这里真正的WTF是Apple创建了一个非可变类的可变子类.在C#/ Java中,您没有可变字符串,而是使用另一个名为StringBuilder的类.
所以,如果这是如此令人困惑,为什么这么多人会copy在NSString物业上使用?因为它很可能是你真正想要的.如果你的班级有一个NSString属性,那么你选择了这个属性,因为你不想担心字符串会从你下面变换出来.要做到这一点,你真的想要一个传入的字符串的副本.如果它是一个NSMutableString你可能想要一个NSString副本,因为你不想给那个可变的字符串,并让它从你下面改变.如果复制一个NSMutableString返回的NSMutableString我不认为人们会使用复制属性,但创建一个自定义setter设置_aString = [[NSString alloc] initWithString: inputString].这可能更清楚,如果你认为它可能是你应该做的.但在这一点上,使用复制NSString属性是常规,所以我可能会继续使用它.你可能会也可能不同意它,但你的问题是为什么人们使用副本然后这就是我亲自做的原因.
编辑2:@David问为什么不以这种方式复制工作?"如果我复制一个字符串,那么我希望后续调用能够更改它." 我想如果你真的想要副作用,你可能应该声明一个没有复制修饰符的NSMutableString(你可能永远不应该使用copy和NSMutableString,但是创建你自己的setter).
在我将近15年的编程中,我真的想不出一个原因,为什么我不希望这个人设置字符串的副作用,但我希望任何老人都能得到字符串的副作用.这真的很奇怪.
此外,我不能为我的生活想到为什么我会想要这种情况,而是将其作为NSString宣传.我的意思是为什么 为什么希望你的不可变属性有时是可变的,但只有当你不能控制的setter(你也不想控制)但你不希望来自设置的对象的副作用时.和你没有告诉任何一个和他们只是弄清楚?我头疼.
我猜这就是为什么Apple做了它做的事情.但这只是猜测.
此外,如果你可以逃脱不可变的数据类型,你应该.它减少了各种复杂性,尤其是多线程.
当然,Objective-C(和大多数OOP语言)让你可以做到这一切,如果你真的想要的话.这取决于程序员和团队.您还可以通过将手机靠在头骨上来测试加速度计.我不认为Apple会推荐这两种方法.