在平时使用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的方法并不出现问题。
-- 目标达成
- 意图:同第一个,我想得到一个不可变的值
使用了strong ,两者使用同一个内存,一旦失手修改了strongMutableNSArry的值,你的不变值strongNSArray也会被修改@property (nonatomic,strong) NSArray * strongNSArray; @property (nonatomic,strong) NSMutableArray *strongMutableNSArry; { strongNSArray = strongMutableNSArry; }
-- 目标不达成
结论
- 因此,结论是,NSString,NSArray,NSDictonary,使用copy属性,而其NSMubtableString,NSMutableArray, NSMutableDictonary属性则使用strong属性