参考链接:
ios深拷贝,浅拷贝,拷贝自定义对象的简单介绍 - daiyelang的专栏 - 博客频道 - CSDN.NET
copy分为深拷贝、浅拷贝。copy的目的是改变副本的时候不影响原对象。
copy的方法有:
- (id)copy;
- (id)mutableCopy;
深拷贝:内容拷贝(对象中的每一个变量都进行了拷贝),创建了新的对象,新对象引用计数为1,原对象引用计数不变。
浅拷贝:指针拷贝,没有创建新对象,原对象引用计数增加1。
copy 返回的是不可变对象,MutableCopy返回的是可变对象。
一个对象想要支持copy就必须遵从 NSCopying 或者 NSMutableCopying
协议,Foundation框架下默认支持copy的对象类型有:NString、NSArray、NSNumber、NSDictionar、NSMutableString、NSMutablerArray、NSMutableDictionary等,我们也可以让自定义对象支持复制,前提就是必须遵循NSCopying或者NSMutableCopying协议。
以下以字符串为例:
1.不可变字符串调用copy实现拷贝。(浅拷贝)
NSString *originalStr = [[NSString alloc] initWithString:@"abcde"];
NSLog(@"originalStr.retainCount %p",originalStr);
NSString *newStr = [originalStr copy];
NSLog(@"originalStr.retainCount %p",originalStr);
NSLog(@"newStr.retainCount %p",newStr);
打印结果如下:
originalStr.memoryAddress 0x100004230
originalStr.memoryAddress 0x100004230
newStr.memoryAddress 0x100004230
newStr的地址和originalStr的地址相同,可知不可变字符串调用copy为浅拷贝(指针拷贝),联想本篇开头说的:copy的目的是改变副本的时候不影响源对象,因为源对象本身就是不可变的,所以这里使用简单的浅拷贝(指针拷贝)即可实现不影响源对象的需求(也是为了性能着想),所有此处copy直接就返回了源对象。
2. 不可变字符串调用mutableCopy实现拷贝 (深拷贝)
NSString *originalStr = [[NSString alloc] initWithString:@"abcde"];
NSLog(@"originalStr.memoryAddress %p",originalStr);
NSMutableString *newStr = [originalStr mutableCopy]; //返回对象是可变的。
NSLog(@"originalStr.memoryAddress %p",originalStr);
NSLog(@"newStr.memoryAddress %p",newStr);
打印地址如下:
originalStr.memoryAddress 0x100004230
originalStr.memoryAddress 0x100004230
newStr.memoryAddress 0x100400000
newStr的地址与originalStr的地址不同,可知是创建了新的对象。是深拷贝
3.可变字符串调用copy(深拷贝)
NSMutableString *originalStr = [NSMutableString stringWithFormat:@"%@",@"abcde"];
NSLog(@"originalStr.memoryAddress %p",originalStr);
NSString *newStr = [originalStr copy];
NSLog(@"originalStr.memoryAddress %p",originalStr);
NSLog(@"newStr.memoryAddress %p",newStr);
打印地址如下:
originalStr.memoryAddress 0x100206d70
originalStr.memoryAddress 0x100206d70
newStr.memoryAddress 0x656463626155
newStr的地址与originalStr的地址不同,可知也进行了深拷贝。
4.可变字符串调用mutableCopy(深拷贝)
NSMutableString *originalStr = [NSMutableString stringWithFormat:@"%@",@"abcde"];
NSLog(@"originalStr.memoryAddress %p",originalStr);
NSMutableString *newStr = [originalStr mutableCopy]; //返回字符串为可变的
NSLog(@"originalStr.memoryAddress %p",originalStr);
NSLog(@"newStr.memoryAddress %p",newStr);
//newStr拼接字符串,查看是否会影响originalStr
[newStr appendFormat:@"just a test!"];
NSLog(@"originalStr=======%@",originalStr);
NSLog(@"newStr============%@",newStr);
打印结果如下:
originalStr.memoryAddress 0x100206d70
originalStr.memoryAddress 0x100206d70 //原可变字符串地址
newStr.memoryAddress 0x100300600 //newStr字符串地址
originalStr=======abcde //newStr拼接后原字符串内容
newStr============abcdejust a test! //newStr拼接后newStr字符串内容
originalStr和newStr的地址不同,说明创建了新的对象。newStr可进行拼接,说明返回的是可变字符串。
结论:只有不可变对象使用copy的时候才是浅拷贝(指针拷贝),其它三种情况均是深拷贝(内容拷贝)。
实现自定义对象的copy:
1 创建model类 House
@property (nonatomic, copy) NSString *houseId
@property (nonatomic, copy) NSString *houseAddress;
@property (nonatomic, copy) NSString *housePics;
2 遵循协议<NSCopying>
3 实现- (id)copyWithZone:(NSZone*)zone方法
- (id)copyWithZone:(NSZone *)zone
{
House *house = [[[self class] allocWithZone:zone] init];
house.houseId = [_houseId copy];
house.houseAddress = [_houseAddress copy];
house.housePics = [_housePics copy];
return house;
}
在使用该类对象的地方即可使用copy复制该对象。
问题思考:在属性中修饰NSString,NSArray等不可变类型的属性为什么用copy比较合适,用strong进行修饰会出现什么问题?
直接上代码。。。
假设House中的houseId使用strong进行修饰:
@property (nonatomic, strong) NSString *houseId;
在main.m文件中实现如下代码:
NSMutableString *muStr = [NSMutableString stringWithFormat:@"%@",@"15000"]; //可变字符串
House *house = [[House alloc] init];
house.houseId = muStr; //为houseId赋值
NSLog(@"house.houseId %@",house.houseId); //第一次houseId的结果
[muStr appendFormat:@"----------16000"]; //修改muStr的内容
NSLog(@"house.houseId %@",house.houseId);
打印结果如下:
house.houseId 15000
house.houseId 15000----------16000
此时的打印结果显示,house.houseId发生了改变,而事实上houseId是NSString类型的数据,程序就会发生隐形bug。至于问题出现的原因在house.m中,代码如下
此时在House.m文件中,setterHouseId事实上是这样的
- (void)setHouseId:(NSString *)houseId
{
if (![_houseId isEqualToString:houseId]) {
[_houseId release];
_houseId = [houseId retain]; //houseId是可变的字符串,strong进行修饰的情况下这里只是进行了额retain操作。只是简单的浅拷贝,所有会出现问题。
}
}
把NSString类型的houseId使用copy进行修饰,打印结果如下:
house.houseId 15000
house.houseId 15000
此时houseId不会发生异常,原因如下:
- (void)setHouseId:(NSString *)houseId
{
if (![_houseId isEqualToString:houseId]) {
[_houseId release];
_houseId = [houseId copy];//在使用copy修饰的情况下,houseId是一个可变的字符串,进行了copy操作,进行了额深拷贝,创建了一个新的不可变字符串赋值给了_houseId,所以每次进行setter操作都创建了一个新的不可变字符串,不会发生问题。
}
}