Objective-C之 深拷贝和浅拷贝、copy和mutableCopy(可以验证下)

基础概念

  • 深拷贝和浅拷贝:是否会产生新对象
    1. 深拷贝:内容拷贝,会产生新的对象
    2. 浅拷贝:指针拷贝,不会产生新的对象
  • copy和mutableCopy:对于产生的对象的可变性而言(前提是产生了新对象)
    1. copy出来的对象不可变
    2. mutableCopy出来的对象可变

验证

非容器类对象

* 对非容器类不可变对象copy

测试代码

NSString *iStr = @"lixue1";
NSString *cpStr = [iStr copy];
NSLog(@"源不可变对象地址:%p  内容%@",iStr,iStr);
NSLog(@"copy出来的对象地址:%p  内容%@",cpStr,cpStr);
NSLog(@"源对象和copy出来的对象地址是否相同: %d",iStr==cpStr?1:0);
NSLog(@"-----------------------------------------");
NSLog(@"对源不可变对象重新赋值");
NSLog(@"-----------------------------------------");
iStr = @"lixue2";//iStr新分配内存空间,cpStr指向原来的内存空间
NSLog(@"源不可变对象地址:%p  内容%@",iStr,iStr);
NSLog(@"copy出来的对象地址:%p  内容%@",cpStr,cpStr);
NSLog(@"源对象和copy出来的对象地址是否相同: %d",iStr==cpStr?1:0);
  1. 运行结果
    这里写图片描述
  2. 结论

对非容器类不可变对象copy
1. copy之后内存地址相同,说明没有产生新的对象,是浅拷贝,指针拷贝,两个指针指向同一块内存。(因为copy出来的对象时不可变的,源对象也不可变,如果再申请一块内存浪费空间,所以苹果做了优化)
2. 对指向对象的源指针重新赋值,会新分配一块新的内存空间,剩下的那个还是指向原来的内存空间,所以是完全独立的两份内存,各自的修改不会对对方产生影响

* 对非容器类可变对象copy

测试代码

NSMutableString *mStr = [[NSMutableString alloc]initWithString:@"lixue"];
NSMutableString *str = [mStr copy];
NSLog(@"源可变对象地址:%p  内容%@",mStr,mStr);
NSLog(@"copy出来的对象地址:%p  内容%@",str,str);
NSLog(@"源对象和copy出来的对象地址是否相同: %d",mStr==str?1:0);
// [str appendString:@" hello"];
  1. 运行结果
    这里写图片描述
    放开注释的那行代码,也就是修改copy出来的对象的值,程序运行时崩溃
    这里写图片描述

这里写图片描述
2. 结论

对非容器类可变对象copy
1. copy之后内存地址发生改变,说明产生了新的对象,是深拷贝
2. 修改copy出来的对象的值,编译时没有报错,运行时崩溃,找不到方法吗,说明copy出来的对象不可以修改(oc的运行时特性决定了编译时可以调用任何对象的任何方法,编译时是声明的类型,也就是NSMutableString,运行时才会决定对象的真实类型是NSString,NSString是NSMutableString的父类,父类中不一定有子类的方法,所以当查找的时候没有找到,程序崩溃)

* 对非容器类不可变对象mutableCopy

测试代码


NSString *iStr = @"lixue1";
NSMutableString *mStr = [iStr mutableCopy];
NSLog(@"源不可变对象的地址:%p  内容%@",iStr,iStr);
NSLog(@"mutableCopy出来对象地址:%p  内容%@",mStr,mStr);
NSLog(@"源对象和mutableCopy出来的对象地址是否相同: %d",iStr == mStr?1:0);
NSLog(@"-----------------------------------------");
NSLog(@"修改mutableCopy出来的对象的值的值");
NSLog(@"-----------------------------------------");
[mStr appendString:@" hello"];
NSLog(@"源不可变对象的地址:%p  内容%@",iStr,iStr);
NSLog(@"mutableCopy出来对象地址:%p  内容%@",mStr,mStr);
NSLog(@"源对象和mutableCopy出来的对象地址是否相同: %d",iStr == mStr?1:0);
  1. 运行结果
    这里写图片描述
  2. 结论

对不可变非容器类对象mutableCopy
1. mutableCopy之后产生了新对象,是深拷贝
2. 修改新对象的值,修改成功,并且对源对象的值没有影响,所以mutableCopy的对象可以修改

* 对非容器类可变对象mutableCopy

测试代码

NSMutableString *mStr = [[NSMutableString alloc]initWithString:@"lixue"];
NSMutableString *mcpStr = [mStr mutableCopy];
NSLog(@"源可变对象的地址:%p  内容%@",mStr,mStr);
NSLog(@"mutableCopy出来的对象的地址:%p  内容%@",mcpStr,mcpStr);
NSLog(@"源对象和mutableCopy出来的对象地址是否相同: NSLog(@"-----------------------------------------");
NSLog(@"修改mutableCopy出来的对象的值");
NSLog(@"-----------------------------------------");
[mStr appendString:@" hello"];
NSLog(@"源可变对象的地址:%p  内容%@",mStr,mStr);
NSLog(@"mutableCopy出来的对象的地址:%p  内容%@",mcpStr,mcpStr);
NSLog(@"源对象和mutableCopy出来的对象地址是否相同 %d",mcpStr == mStr?1:0);
  1. 运行结果
    这里写图片描述
  2. 结论

对可变非容器类对象mutableCopy
1. mutableCopy之后产生了新对象,是深拷贝
2. 修改新对象的值,修改成功,对原来对象没影响,说明mutableCopy出来的对象可以修改

非容器类对象总结

  1. 对不可变对象copy,是浅拷贝,copy出来对象不可变
  2. 对可变对象copy,是深拷贝,拷贝出来的对象不可变
  3. 对不可变对象mutableCopy,是深拷贝,新对象可变
  4. 对可变对象mutableCopy,是深拷贝,新对象可变

容器类对象

* 对不可变容器类对象copy

测试代码

NSArray *iarr = @[[NSMutableString stringWithString:@"hello "],@"2",@"3"];
NSArray *cpArr = [iarr copy];
NSLog(@"源容器类对象地址:%p  内容:%@",iarr,iarr);
NSLog(@"copy出来的容器类对象的地址:%p  内容:%@",cpArr,cpArr);
NSLog(@"源容器类对象和copy出来的容器类对象的地址是否相等: %d",iarr==cpArr?1:0);
NSLog(@"源容器类对象中的元素的地址:%p  内容:%@",iarr[0],iarr[0]);
NSLog(@"copy出来的容器类对象中的元素的地址:%p  内容:%@",cpArr[0],cpArr[0]);
NSLog(@"源容器类对象中的元素的地址和copy出来的容器类对象中的元素的地址是否相等: %d",iarr[0]==cpArr[0]?1:0);
NSLog(@"-----------------------------------------");
NSLog(@"给源容器类对象重新赋值");
NSLog(@"-----------------------------------------");
iarr = @[[NSMutableString stringWithString:@"444 "],@"5",@"6"];//iarr新分配内存空间,cpArr指向原来的内存空间
NSLog(@"源容器类对象地址:%p  内容:%@",iarr,iarr);
NSLog(@"copy出来的容器类对象的地址:%p  内容:%@",cpArr,cpArr);
NSLog(@"源容器类对象和copy出来的容器类对象的地址是否相等: %d",iarr==cpArr?1:0);
NSLog(@"源容器类对象中的元素的地址:%p  内容:%@",iarr[0],iarr[0]);
NSLog(@"copy出来的容器类对象中的元素的地址:%p  内容:%@",cpArr[0],cpArr[0]);
NSLog(@"源容器类对象中的元素的地址和copy出来的容器类对象中的元素的地址是否相等: %d",iarr[0]==cpArr[0]?1:0);
  1. 运行结果
    这里写图片描述
  2. 结论
    对不可变容器类对象copy
    1. copy之后内存地址没有发生改变,没有产生新对象,所以是浅拷贝(原因同非容器类对象)
    2. 重新赋值之后地址发生改变,让原来的指针指向了新的内存空间,另一个没有哦重新赋值的还是指向原来的内存空间,凉粉完全独立的内存
    3. 对不可变容器类对象copy时,里面的元素地址也没有发生改变,所以是浅拷贝

* 可变容器类对象copy

测试代码

NSMutableArray *marr = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"hello "],@"2", nil];
NSMutableArray *cpmarr = [marr copy];
NSLog(@"源可变容器类对象地址:%p  内容:%@",marr,marr);
NSLog(@"copy出来的对象的地址:%p  内容:%@",cpmarr,cpmarr);
NSLog(@"源可变容器类对象地址和copy出来的对象的地址是否相等: %d",marr ==cpmarr?1:0);
NSLog(@"源可变容器类对象中的元素的地址:%p  内容:%@",marr[0],marr[0]);
NSLog(@"copy出来的对象中的元素的地址:%p  内容:%@",cpmarr[0],cpmarr[0]);
NSLog(@"源可变容器类对象中元素的地址和copy出来的容器类对象的元素的地址是否相等: %d",[marr[0] isEqual:cpmarr[0]]?1:0);
[cpmarr[0] appendString:@"lixue"];
NSLog(@"-----------------------------------------");
NSLog(@"修改copy出来的容器类对象的第0号元素的值");
NSLog(@"-----------------------------------------");

NSLog(@"源可变容器类对象地址:%p  内容:%@",marr,marr);
NSLog(@"copy出来的对象的地址:%p  内容:%@",cpmarr,cpmarr);
NSLog(@"源可变容器类对象地址和copy出来的对象的地址是否相等: %d",marr ==cpmarr?1:0);
NSLog(@"源可变容器类对象中的元素的地址:%p  内容:%@",marr[0],marr[0]);
NSLog(@"copy出来的对象中的元素的地址:%p  内容:%@",cpmarr[0],cpmarr[0]);
NSLog(@"源可变容器类对象中元素的地址和copy出来的容器类对象的元素的地址是否相等: %d",[marr[0] isEqual:cpmarr[0]]?1:0);
//        [cpmarr addObject:@"88"];
  1. 运行结果
    这里写图片描述
    去掉最后一行的注释(也就是修改copy出来的容器类对象),运行时崩溃
    这里写图片描述
    这里写图片描述
  2. 结论
    对可变容器类对象copy
  3. copy之后产生了新的容器类对象,是深拷贝
  4. copy之后元素的地址没有发生改变,所以容器内元素的拷贝还是浅拷贝。copy出来的容器中的元素的改变会影响原来对象中的元素的值
  5. copy出来的对象不可变(原因同非容器类对象)

* 对不可变容器类对象mutableCopy

测试代码

NSArray *iarr = @[[NSMutableString stringWithString:@"hello "],@"2",@"3"];
NSMutableArray *mcpArr = [iarr mutableCopy];
NSLog(@"源不可变容器类对象地址:%p  内容:%@",iarr,iarr);
NSLog(@"mutableCopy出来的对像的地址%p  内容:%@",mcpArr,mcpArr);
NSLog(@"源不可变容器类对象的地址和mutableCopy出来的对象的地址是否相同: %d",iarr ==mcpArr?1:0);
NSLog(@"源不可变容器类对象中的元素的地址:%p  内容:%@",iarr[0],iarr[0]);
NSLog(@"mutableCopy出来的容器对象中的元素的地址:%p  内容:%@",mcpArr[0],mcpArr[0]);
NSLog(@"源不可变容器类对象中元素的地址和mutableCopy出来的容器中元素的地址是否相同:%d",iarr[0] ==mcpArr[0]?1:0);
NSLog(@"-----------------------------------------");
NSLog(@"修改mutableCopy出来的容器类对象的第0号元素的值");
NSLog(@"-----------------------------------------");
[mcpArr[0] appendString:@"lixue"];
NSLog(@"源不可变容器类对象地址:%p  内容:%@",iarr,iarr);
NSLog(@"mutableCopy出来的对像的地址%p  内容:%@",mcpArr,mcpArr);
NSLog(@"源不可变容器类对象的地址和mutableCopy出来的对象的地址是否相同: %d",iarr ==mcpArr?1:0);
NSLog(@"源不可变容器类对象中的元素的地址:%p  内容:%@",iarr[0],iarr[0]);
NSLog(@"mutableCopy出来的容器对象中的元素的地址:%p  内容:%@",mcpArr[0],mcpArr[0]);
NSLog(@"源不可变容器类对象中元素的地址和mutableCopy出来的容器中元素的地址是否相同:%d",iarr[0] ==mcpArr[0]?1:0);
  1. 运行结果
    这里写图片描述
  2. 结论
    对不可变容器类对象mutableCopy
    1. mutableCopy之后会产生新的容器类对象,是深拷贝
    2. mutableCopy之后容器内元素的拷贝还是浅拷贝,没有mutableCopy出来新的元素,所以元素的拷贝是浅拷贝

* 对可变容器类对象mutableCopy

测试代码

NSMutableArray *marr = [NSMutableArray arrayWithObjects:[NSMutableString stringWithString:@"hello "],@"2", nil];
NSMutableArray *mcpmarr = [marr mutableCopy];

NSLog(@"源可变容器类对象地址:%p  内容:%@",marr,marr);
NSLog(@"mutableCopy出来的对像的地址%p  内容:
NSLog(@"源可变容器类对象的地址和mutableCopy出来的对象的地址是否相同: %d",marr ==mcpmarr?1:0);
NSLog(@"源可变容器类对象中的元素的地址:%p  内容:%@",marr[0],marr[0]);
NSLog(@"mutableCopy出来的容器对象中的元素的地址:%p  内容:%@",mcpmarr[0],mcpmarr[0]);
NSLog(@"源可变容器类对象中元素的地址和mutableCopy出来的容器中元素的地址是否相同: %d",marr[0]==mcpmarr[0]?1:0);
NSLog(@"-----------------------------------------");
NSLog(@"修改mutableCopy出来的容器类对象的第0号元素的值");
NSLog(@"-----------------------------------------");
[mcpmarr[0] appendString:@"lixue"];
NSLog(@"源可变容器类对象地址:%p  内容:%@",marr,marr);
NSLog(@"mutableCopy出来的对像的地址%p  内容:%@",mcpmarr,mcpmarr);
NSLog(@"源可变容器类对象的地址和mutableCopy出来的对象的地址是否相同: %d",marr ==mcpmarr?1:0);
NSLog(@"源可变容器类对象中的元素的地址:%p  内容:%@",marr[0],marr[0]);
NSLog(@"mutableCopy出来的容器对象中的元素的地址:%p  内容:%@",mcpmarr[0],mcpmarr[0]);
NSLog(@"源可变容器类对象中元素的地址和mutableCopy出来的容器中元素的地址是否相同: %d",marr[0]==mcpmarr[0]?1:0);
  1. 运行结果
    这里写图片描述
  2. 结论
    对可变容器类对象的mutableCopy
  3. 会产生新的容器类对象,是深拷贝
  4. 容器类对象里面的元素的地址没有发生改变,所以还是浅拷贝

容器类对象总结

  1. 源容器类对象不可变,copy之后的也不可变,不会产生新对象,是浅拷贝,重新赋值会新分配内存(同非容器类)
  2. 源容器类可变,copy之后产生新对象,深拷贝(非容器类)
  3. 源容器类不可变,mutableCopy产生新对象,深拷贝(非容器类)
  4. 源容器类可变,mutableCopy产生新对象,深拷贝(非容器类)
  5. 容器类对象的元素的拷贝都是浅拷贝

自定义对象

* copy:遵守协议,实现copyWithZone方法
  1. 自定义Person类,重写copyWithZone方法
-(id)copyWithZone:(NSZone *)zone{
    Person * p = [[Person alloc]init];
    p.name = self.name;
    return p;
}
  1. 在main中使用
Person *p = [[Person alloc]init];
p.name = @"lixue";
Person *cp = [p copy];
NSLog(@"源自定义对象地址:%p 自定义对象的属性地址:%p 内容%@",p,p.name,p.name);
NSLog(@"copy出来的自定义对象地址:copy出来的自定义对象地属性址:%p %p 内容%@",cp,cp.name,cp.name);
  1. 运行结果
    这里写图片描述
  2. 结论:自定义对象的copy是深拷贝,会产生新的对象,但是属性的拷贝还是浅拷贝,指向同一块内存

* mutableCopy:

这个没想出来有什么用?

总结

  1. 不管是容器类还是非容器类对象
  2. 对不可变对象copy,又因为copy之后的对象本身不可变,所以没有必要申请新内存,苹果做了优化,指向同一块内存,所以是浅拷贝
  3. 对可变对象copy,会产生新对象,是深拷贝
  4. 对不可变对象mutableCopy,会产生新对象,是深拷贝
  5. 对可变对象mutbaleCopy,会产生新对象,是深拷贝
  6. 不管容器类对象时深拷贝驾驶浅拷贝,里面的元素都是浅拷贝
  7. 自定义对象要copy,需要遵守协议,实现copyWithZone方法,会产生新对象,是深拷贝,但是属性的 拷贝仍然是浅拷贝

猜你喜欢

转载自blog.csdn.net/li15809284891/article/details/77585012