先说字符串.假设对象持有如下两个属性
@property (nonatomic, strong) NSString *strStrong;
@property (nonatomic, copy) NSString *strCopy;
然后做如下操作1
```
NSMutableString *str = [NSMutableString stringWithFormat:@"%@",@"3333"];
self.strCopy= str;
self.strStrong= str;
NSLog(@"\n%p\n%p\n%p",str,self.strStrong,self.strCopy);
```
得到的结果如下
0x6000038e9b30
0x6000038e9b30
0xcefcc275fcf19ade
也就是说对于可变字符串,关键字strong只是引用计数器加1,strong后的指针指向原来的存储空间。copy后是新开一个存储空间。而且很重要的一点,copy的对象类型是不可变字符串,就是NSString类型。那strong不用说啦,还是可变类型。
那么对于NSString,使用copy和strong用什么不同吗?请看下面例子
NSString*str =@"33";
self.strCopy= str;
self.strStrong= str;
NSLog(@"\n%p\n%p\n%p",str,self.strStrong,self.strCopy);
结果如下
0x107cd8078
0x107cd8078
0x107cd8078
就是说,对于NSString,不管使用copy还是strong,都只是引用计数器加1。不会开辟新的内存空间。而且类型都是NSString类型的。就是说对于NSString类型,你的关键字是copy还是strong,影响不是那么大。但是考虑到多态特性,NSString类型可能指向NSMutableString类型的对象,建议字符串类型还是使用copy吧。
总结如下:使用copy之后,对于不可变字符串来说,不会新开内存空间,跟strong作用一样。但是对于可变字符串来说,copy是新开一个内存空间的,且新创建的对象是不可变字符串,strong是引用计数器加1,类型不变。
2.再说下数组
NSArray*arr =@[@"23",@"45"];
NSArray*arr2 = [arr copy];
NSArray*arr3 = [arr mutableCopy];
NSLog(@"%p\n%p\n%p",arr,arr2,arr3);
对应的值如下
0x6000036e7220
0x6000036e7220
0x6000038e9800
说明,不可变数组使用copy只是浅拷贝,就是原对象引用计数器加1,在内存中没有新开辟一个空间。而mutableCopy,原对象引用计数器不累加,但是新开了一个内存空间,是个深拷贝。而且copy后的类型还是不可变数组,mutableCopy之后的对象类型是可变数组
那么对于可变数组的copy和mutableCopy又是怎么样?请看下面的例子
NSMutableArray *arrM = [NSMutableArray arrayWithObjects:@"33",@"55", nil];
NSArray*arrM2 = [arrM copy];
NSArray*arrM3 = [arrM mutableCopy];
NSLog(@"%p\n%p\n%p",arrM,arrM2,arrM3);
对应的值如下
0x6000038e9b00
0x6000036e7160
0x6000038e9860
说明可变数组不管使用copy还是mutableCopy,都会新开一个内存空间。而且copy后的对象类型是不可变数组,mutableCopy之后的是可变数组。也就是说可变数组的的拷贝都是深拷贝
总结如下:对于不可变数组,copy只是引用计数器加1,不开辟新的内存空间,跟strong作用一样。mutableCopy开辟新的内存空间。对于可变数组,不管使用copy还是mutableCopy,都会新开一个内存空间。而且copy后的对象类型是不可变数组,mutableCopy之后的是可变数组。
对于字符串和数组使用copy的共同特点是,不可变类的使用copy只是引用计数器加1;可变类型的使用copy会新开一个内存空间,但是类型是不可变类型。
strong对于可变数组只是引用计数器加1.有兴趣的朋友可以在MRC环境下验证。
属性修饰符字符串为什么用copy而不用strong
在Objective-C中,属性修饰符选择copy
而不是strong
,主要是基于几个关键考虑因素,特别是当属性类型为不可变对象(如NSString
、NSArray
、NSDictionary
等)时。以下是几个主要原因:
防止可变对象被意外修改:当你将一个可变对象(如
NSMutableString
)赋值给一个被声明为不可变类型(如NSString
)的属性时,如果使用strong
修饰符,那么原始的可变对象仍然可以被修改,这可能会导致不可预见的行为或错误。使用copy
修饰符可以确保属性接收的是原始对象的一个不可变副本,因此即使原始对象被修改,属性的值也不会受到影响。符合不可变语义:对于被设计为不可变的类(如
NSString
),使用copy
修饰符更符合其不可变的语义。这有助于维护类的封装性和不变性,使得类的使用者不会意外地修改到类的内部状态。线程安全:在某些情况下,使用
copy
可以提高线程安全性。虽然strong
和copy
本身并不直接解决线程同步问题,但当你将属性设置为copy
时,每个线程都会获得其自己的对象副本,从而减少了线程间共享数据的风险。性能考虑:虽然
copy
操作可能会比简单的赋值(strong
)更耗时,因为它涉及到对象的复制,但在许多情况下,这种性能开销是可以接受的,并且换来了更高的数据完整性和安全性。此外,对于不可变对象来说,由于它们的内容在创建后不会改变,因此它们的copy
操作通常是非常高效的(例如,NSString
的copy
方法可能只是简单地返回一个指向相同数据的指针)。
需要注意的是,对于可变对象类型(如NSMutableString
、NSMutableArray
等),你应该使用strong
修饰符,因为copy
将创建一个不可变的副本,这可能会与你的设计意图相悖。
最后,选择copy
还是strong
应该基于你的具体需求和属性的预期用途。在大多数情况下,对于不可变对象类型,使用copy
是一个更安全、更可靠的选择。