浅拷贝和深拷贝的区别在于拷贝后是重新复制了一个新对象,还是说只是增加了一个新的指向原对象的指针.
这里以字符串和数组,字典为例.
1.对于不可变的字符串,数组和字典来说,向其发送 copy 信息,即[obj copy],拷贝后得到的是一个不可变的对象,并且拷贝并没有将原对象进行复制.这是浅拷贝.
//对于不可变的 NSString, NSArray, NSDictionary,进行拷贝,是浅拷贝,得到的是不可变的对象
NSString * str = [NSString stringWithFormat:@"hello world"];
NSLog(@"str 引用计数 %lu", [str retainCount]);
NSLog(@"str 地址 %p", str);
NSString * newstr = [str copy]; //浅拷贝,原对象内容并没有再复制一份,只是拷贝出一个新指针,指向同一对象.因此原对象的引用计数+1.
NSLog(@"newstr 引用计数%lu", [newstr retainCount]);
NSLog(@"copy 后 str 引用计数 %lu", [str retainCount]);
NSLog(@"newstr 地址 %p", newstr);
打印结果如下:
2015-02-05 17:13:23.259 LessonUI08Control[3652:154482] str 引用计数 1
2015-02-05 17:13:23.260 LessonUI08Control[3652:154482] str 地址 0x7ff3b97315e0
2015-02-05 17:13:23.260 LessonUI08Control[3652:154482] newstr 引用计数2
2015-02-05 17:13:23.260 LessonUI08Control[3652:154482] copy 后 str 引用计数 2
2015-02-05 17:13:23.260 LessonUI08Control[3652:154482] newstr 地址 0x7ff3b97315e0
可以看出,拷贝后 newstr 的地址和 str 的地址是一样的,说明 str 并没用完全复制一份, 只是新多了一个指针指向它,因此 str 的引用计数+1.
2.对于不可变的字符串,数组和字典来说,向其发送 mutableCopy 信息,即[objmutableCopy],拷贝后得到的是一个可变的对象,并且拷贝结果是对原对象进行了复制,得到了一个新的和原对象完全相同的新对象.这是深拷贝
NSString * str = [NSString stringWithFormat:@"hello world"];
NSLog(@"str 引用计数 %lu", [str retainCount]);
NSLog(@"str 地址 %p", str);
NSMutableString * newstr = [str mutableCopy]; //使用 mutableCopy 可以进行深拷贝,并且拷贝出的字符串是可变的.
NSLog(@"newstr 引用计数 %lu", [newstr retainCount]);
[newstr deleteCharactersInRange:NSMakeRange(2, 2)];
NSLog(@"str 引用计数 %lu", [str retainCount]);
NSLog(@"newstr 地址 %p", newstr);
NSLog(@"%@", newstr);
打印结果如下:
2015-02-05 17:17:46.487 LessonUI08Control[3680:155935] str 引用计数 1
2015-02-05 17:17:46.487 LessonUI08Control[3680:155935] str 地址 0x7fbc82c8b300
2015-02-05 17:17:46.487 LessonUI08Control[3680:155935] newstr 引用计数 1
2015-02-05 17:17:46.488 LessonUI08Control[3680:155935] str 引用计数 1
2015-02-05 17:17:46.488 LessonUI08Control[3680:155935] newstr 地址 0x7fbc82f11ff0
2015-02-05 17:17:46.488 LessonUI08Control[3680:155935] heo world
可以看出,拷贝后 newstr 的地址和 str 的地址不一样,说明 newstr 指向的是一个新对象,并且 str 的引用计数并没有增加, newstr 指向一个新对象,引用计数+1.并且拷贝后得到的 newstr 是可变的,可以对其进行修改.
3.向可变字符串,数组,字典发送 copy 信息,拷贝是深拷贝,返回的是不可变对象.
NSMutableString * str = [NSMutableString stringWithFormat:@"hello world"];
NSLog(@"str 引用计数 %lu", [str retainCount]);
NSLog(@"str 地址 %p", str);
NSString * newstr = [str copy]; //深拷贝,原对象内容完全复制一份新的, newstr 保存新对象的地址,新对象引用计数+1.
NSLog(@"newstr 引用计数 %lu", [newstr retainCount]);
NSLog(@"newstr 地址 %p", newstr);
// [newstr deleteCharactersInRange:NSMakeRange(2, 2)]; //会报错,因为拷贝出来的 newstr 是不可变的
NSLog(@"%@", newstr);
打印结果如下:
2015-02-05 17:31:47.737 LessonUI08Control[3767:160881] str 引用计数 1
2015-02-05 17:31:47.738 LessonUI08Control[3767:160881] str 地址 0x7fb593f0af70
2015-02-05 17:31:47.738 LessonUI08Control[3767:160881] newstr 引用计数 1
2015-02-05 17:31:47.738 LessonUI08Control[3767:160881] newstr 地址 0x7fb593e0b930
2015-02-05 17:31:47.738 LessonUI08Control[3767:160881] hello world
可以看出 newstr 和 str 的地址是不同的,并且 str 的引用计数没有因为 copy 而增加
4.对可变字符串,数组,字典发送 mutableCopy 信息,得到的是深拷贝,并且返回的对象也是可变的.
总之, copy 返回的是不可变对象, mutableCopy 返回的是可变对象.
copy 用在不可变和可变对象上都是浅拷贝,
mutableCopy 用在可变和不可变对象上都是深拷贝.
应用举例
假设现在有一个 Person 类, Person 有一个属性 name.
@property (nonatomic, retain)NSString * name; //这里先定义为 retain
这里,虽然我们要求的 name 是一个不可变字符串,但并不能保证别人在使用我们的类的时候为 name 赋值一个可变字符串.如下:
Person * p1 = [[Person alloc] init];
NSMutableString * str = [NSMutableString stringWithFormat:@"mike"];
p1.name = str; //当 name 属性设置为 copy 的时候,进行赋值的时候会执行 _name = [name copy],即会进行一次拷贝,而且对于可变字符串来说,是深拷贝,因此属性 _name 指向的是一个新对象,对原对象 str 进行的任何更改都不会影响 _name 的值
[str deleteCharactersInRange:NSMakeRange(2, 2)];
[p1 sayhi];//打印 name 的值
[p1 release];
NSLog(@"str = %@", str);
str 是一个可变字符串,在为 name 赋值之后,又对 str 进行了更改,打印后结果如下:
2015-02-05 17:41:40.035 LessonUI08Control[3806:163843] name = mi
2015-02-05 17:41:40.036 LessonUI08Control[3806:163843] str = mi
我们发现, name 的值因为 str 的变化而发生了变化,这是因为现在 name 和 str 指向的是同一个对象,当对该对象进行了更改,会影响他们2个的值,这显然不是我们希望看到的.为了避免这种情况出现,我们就需要将属性修饰符改为 copy, 从而保证 name 中所存的是一个 str 的副本.当改为 copy 后,属性 name 的 setter 方法如下:
- (void)setName:(NSString *)name
{
if (_name != name) {
[_name release];
_name = [name copy];
}
}
可以看出,当传入的 name 是一个可变对象的时候, copy 后就能得到一个新的完全相同的对象.