-
浅拷贝:不拷贝对象本身,仅仅是拷贝指向对象的指针(copy)
-
深拷贝:直接拷贝整个对象的内存到另一块内存上去(mutableCopy)
以下特殊情况:
- 不可变情况下被拷贝指针重新赋值。
NSString *str1 = @"str1";
NSString *str2 = [str1 copy];
str1 = @"asdf";
NSLog(@"\nstr1 = %@ str1P = %p \n str2 = %@ str2P = %p", str1, str1, str2, str2);
/*输出结果,修改str2 同理
str1 = asdf str1P = 0x10776b1a0
str2 = str1 str2P = 0x10776b180
*/
开始时str1和str2指向的是相同的内存空间,且两者都是不可变的所以系统只会生成一个指向同一内存空间的指针,但是如果重新给给str1或者str2赋值,之前内容不可变而且双方应互不影响,所以只能重新开出一块内存空间。
- 可变情况下的浅拷贝
NSMutableArray *mArr1 = [@[@"123", @"456", @"asd"] mutableCopy];
NSMutableArray *mArr2 = [mArr1 copy];
NSLog(@"\n mArr1 = %@ mArr1P = %p mArr1 class = %@ \n\n mArr2 = %@ mArr2P = %p mArr2 class = %@", mArr1, mArr1, [mArr1 class], mArr2, mArr2, [mArr2 class]);
/*输出结果
mArr1 = (
123,
456,
asd
)
mArr1P = 0x60400025db20
mArr1 class = __NSArrayM
mArr2 = (
123,
456,
asd
)
mArr2P = 0x60400025dd30
mArr2 class = __NSArrayI
*/
此时mArr1指向的内存空间是可变的,如果对mArr1进行修改,同一内存空间的内容就会发生
变化。遵循互不影响的原则,加上mArr2是不可变的(虽然指针类型是NSMutable但是是copy拷贝所以不可变,同时如果给mArr2增删元素系统会崩,因为是用copy拷贝则意味着其不可变),所以copy会开辟新的内存空间。
- 用copy修饰一个属性后就限制了他是不可变数组(因为copy就是复制一个不可变内存的数组),mutableCopy是深拷贝重新赋内存,而如果调用copy或直接指向arr则不会。(但如果arr是mutable类型则仍是第二个问题,无论如何都是地址都是不同的,因为arr可变)
注:copy属性修饰符是深拷贝,即为成员变量复制一块新的对象实例在别的内存并让成员变量持有它。
//
@property (nonatomic, copy) NSMutableArray *array;
//
NSArray *arr = @[@"123", @"456", @"asd"];
self.mArr = [arr mutableCopy];
NSLog(@"\n arrP = %p \n self.mArrP = %p, self.mArr class = %@", arr, self.mArr, [self.mArr class]);
/* 输出结果
arrP = 0x600000eed530
self.mArrP = 0x600000eee520
*/
总结:
- 用copy修饰的 或者赋值的 变量肯定是不可变的。
- 用copy赋值,要看源对象是否是可变的,来决定只拷贝指针,还是也拷贝对象到另一块内存空间
- 对象之间mutableCopy赋值,肯定会拷贝整个对象内存到另一块内存中
- 对象之间赋值之后,再改变,遵循互不影响的原则
PS:同时在加一点assign的用法:
assign修饰属性只能修饰基本变量类型以及基本的oc类型,不能修饰对象!!!
如果用assign修饰对象,当对象释放后(因为不存在强引用,离开作用域对象内存可能被回收),指针的地址还是存在的,也就是说指针并没有被置为nil,下次再访问该对象就会造成野指针异常。对象是分配在堆上的,堆上的内存由程序员手动释放。
所以assign修饰的是存在栈上的基本类型,系统会自动回收。