- 思考下的一些问题总结,如有错误,请斧正。(多谢@. . 的纠正)
在平时使用NSArray,NSDictionary以及NSString的过程中,经常会默认写了strong 属性,或者按照规范使用Copy,在Apple的规范中,是提倡我们使用copy的属性。
基本原理
- 简单来说,copy属性就像字面意思一样,在赋值的时候会系统自动copy一份内存出来,修改新的变量,并不会导致原先的变量出现改变
即:
@property (nonatomic,strong) NSMutableString *strCopyA;
@property (nonatomic,copy) NSMutableString *strCopyB;
{
self.strCopyA = [NSMutableString stringWithFormat:@"strCopyA"];
self.strCopyB = self.strCopyA;
}
对比内存地址:
strB - 0x7f97da5a0cd0
strA - 0x7f97da5a0e30
从内存地址,我们就可以看出,两者
但是同学们需要注意到这里用到的mutable 可变容器,而不是我们平时用的NSString
- NSString 属性下使用copy 和 strong 其实是一个作用的,copy并不会起作用,因为NSString是不可变的,所以copy没用
copy和strong 实际在的差异就在于setter的生成的方式不一样,可以简单的看下面(ARC模式下…),但是这个是针对mutable容器的
//copy
- (instancetype)setStr:(NSMutableString *)str
{
_str = [str copy];
return _str;
}
//strong
- (instancetype)setStr:(NSMutableString *)str
{
_str = str;
return str;
}
- 从而可以看出两者的差别:开辟新内存,和新建一个计数引用
测试:
NSMutableString *mutStr = [NSMutableString stringWithFormat:@"原始"];
self.strCopy = mutStr;
self.strStrong = mutStr;
NSLog(@"-----------修改----------------------");
[mutStr appendString:@" + 添加了"];
结果:可以看到只有Copy的那一栏的内存地址改动了,所以修改原来的变量,并不会导致新Copy的内存被修改,然而在Strong下,因为是同一块内存,仅仅是retain count +1 了而已,所以还是持有同一块内存,修改会同时显示在两个对象上
mutStr = 原始 - 0x7ff27ac531f0
strCopy = 原始 - 0x7ff27ac48b40
strStrong = 原始 - 0x7ff27ac531f0
-----------修改----------------------
mutStr = 原始 + 添加了 - 0x7ff27ac531f0
strCopy = 原始 - 0x7ff27ac48b40
strStrong = 原始 + 添加了 - 0x7ff27ac531f0
- NSString是NSMutableString的父类,父类可以接受子类的类型,因此当NSString 设定为copy的时候,它会在接受NSmutableString 的时候起到效果,从而才赋值的是另外copy了一份,不至于让 NSString 在调用mutable的方法的时候 产生错误。
测试:
self.strA = [NSMutableString string];
self.strB = [NSMutableString stringWithString:@"B"];
[self.strA setString:self.strB]; // 崩溃位置
self.strB = self.strA;
原因: self.strB真实类型是NSString不可变,因为strB使用copy属性,在赋值B的时候,@”B”为NSString类型,因此导致真实类型被修改,所以在NSString调用NSMutableString的方法会异常。
copy 和 mutableCopy方法
实际上,@property (nonatomic,copy) 只有copy属性,而没有mutableCopy的修饰符,因此,[str copy]执行完的都是immutable不可变类型,所以我们看copy是否起作用,主要是看Setter方法中传入的参数的真实类型来决定是否copy开辟可以内存空间,因为[immutable copy]是浅拷贝,[mutable copy]是深拷贝
直观点由setter方法 内部查看…
//@property (nonatomic,copy) NSMutableString *disStr;
- (void)setDisStr:(id)sourceStr
{
_disStr = [sourceStr copy];//取决于sourceStr的类型,如果是immutable则为浅拷贝,如果是mutable则是深拷贝
//返回的类型为immutable,不能调用mutable的方法了(runtime)
}
- 如果需要用mutableCopy 的方法 就唯有自己去重写setter 方法了
初步结论
- NSString 使用 copy, 而NSMutableString则推荐使用strong,避免埋下地雷炸死自己。
浅拷贝(shallow copy)和深拷贝(deep copy)
浅拷贝,只是对指针的拷贝,拷贝后两个指针指向同一个内存空间,深拷贝不但对指针进行拷贝,而且对指针指向的内容进行拷贝,经深拷贝后的指针是指向两个不同地址的指针
什么时候使用?
- 无论深浅,都是得根据需求而去决定怎么使用。当深拷贝发生时,通常表明存在着一个“聚合关系”,而浅拷贝发生时,通常表明存在着一个“相识关系”。
- 是深拷贝还是浅拷贝,并不是取决于时间效率、空间效率或是语言等等,而是取决于哪一个是逻辑上正确的。
- 总的来说,一句话就是什么场景决定在怎么使用,然而请看下面的场景使用:
场景使用
NSArray 和 NSString实际是都是一样的效果,如果是在使用NSMutableArray 的时候使用copy 的值,那在例如tableview 下的dataSource,在赋值的时候,会另外mutableCopy一份新的,前后不干扰。
我们假设一个意图,一般情境下,我们使用NSArray类型就是为了得到一个不可变的类型,也不希望有人再去改动,否则我们是会使用NSMutableArray去修改需要改动的array;
我们解释这个意图:我想得到一个不可变的copyNSArray,也不希望其他人修改:
@property (nonatomic,copy) NSArray *copyNSArray;
@property (nonatomic,strong) NSMutableArray *strongMutableNSArry;
{
copyNSArray = strongMutableNSArry;
}
我们根据这个行为,判断你是需要一个新的值,从而让后续的操作不对原先的值进行更新,因为你使用的NSArray不可变类型,这时候,因为copyNSArray copy 了 strongMutableNSArry,因此两者已经不相关。
– 目标达成
- 意图:我想得到一个可改变的array,希望可以在其他地方修改
@property (nonatomic,strong) NSMutableArray *strongMutableNSArryA;
@property (nonatomic,strong) NSMutableArray *strongMutableNSArryB;
{
strongMutableNSArryA = strongMutableNSArryB;
}
使用strong的情况下,strongMutableNSArryA = strongMutableNSArryB,两者指向同一个内存块,因此我们实际上是在使用同一个可变string,这个情况下使用mutable的方法并不出现问题。
– 目标达成
- 意图:同第一个,我想得到一个不可变的值
@property (nonatomic,strong) NSArray * strongNSArray;
@property (nonatomic,strong) NSMutableArray *strongMutableNSArry;
{
strongNSArray = strongMutableNSArry;
}
使用了strong ,两者使用同一个内存,一旦失手修改了strongMutableNSArry的值,你的不变值strongNSArray也会被修改
– 目标不达成
结论
- 因此,结论是,NSString,NSArray,NSDictonary,使用copy属性,而其NSMubtableString,NSMutableArray, NSMutableDictonary属性则使用strong属性