< 返回首页

标哥的笔记,是记录在日常学习技术和日常开发中那些年遇到过的坑!本站为新站,原"标哥的技术博客"中的文章会慢慢移到本站,欢迎收藏本站!
在使用本站过程中,有任何建议请联系标哥! 另,承接App开发、网站开发和微信小程序开发!欢迎联系我们


iOS Copy/MutableCopy细讲

 作者:标哥    发布日期:2017-01-12 22:10    阅读量:886次
 

在OC语法中,提供了Copy语法(Copy/MutableCopy)用于对象的拷贝。其中很容易混淆的是浅拷贝和深拷贝。所谓浅拷贝,即单纯的地址拷贝,并不产生新的对象,而是对原对象的引用计数值加1;而深拷贝,即是对象拷贝,产生新的对象副本,拥有独立的内存空间,引用计数器为1。

我们通过一张图先来看看深拷贝与浅拷贝的区别:

系统类对象Copy/NSMutableCopy

我们常用的有NSString/NSMutableString、NSArray/NSMutableArray、NSDictionary/NSMutableDictionary、NSSet、NSMutableSet等系统类对象,下面我们来验证什么情况下是深拷贝,什么情况下是浅拷贝。

1.1 mutableCopy不一定是深拷贝

先来测试一下NSString与NSMutableString:

NSString *str1 = @"str1";
NSMutableString *str2 = [str1 mutableCopy];
NSLog(@"str1=%p str2=%p", str1, str2);
  
NSMutableString *str3 = [[NSMutableString alloc] initWithString:@"sdfsdf"];;
NSMutableString *str4 = [str1 mutableCopy];
NSLog(@"str3=%p str4=%p", str3, str4);

// 打印结果:
// str1=0x10516e068 str2=0x7fdd73425bd0
// str3=0x7fdd7373d360 str4=0x7fdd7373a190

从打印结果可以看出来,对于NSString与NSMutableString的mutableCopy:

NSString类型对象调用mutableCopy得到的新对象,拥有独立的内存空间,因此是深拷贝。NSMutableString类型对象调用mutableCopy得到的新对象,拥有独立的内存空间,因此是深拷贝

测试NSArray/NSMutableArray的情况如何:

NSArray *array = @[@"array", @"array1"];
NSMutableArray *array1 = [array mutableCopy];
NSLog(@"array=%p, array1=%p", array, array1);
  
for (id obj in array) {
    NSLog(@"%p", obj);
}
  
for (id obj in array1) {
    NSLog(@"%p", obj);
}

// 打印结果:
array=0x7fdf196055c0, array1=0x7fdf19695fa0

0x105a66088
0x105a660a8

0x105a66088
0x105a660a8

由此可以知道,NSArray对象调用mutableCopy会重新新的可变数组对象,但是可变数组对象的元素并没有独立的内存空间,只是地址的复制而已,因此只是对数组进行了深拷贝,而对数组元素却是进行浅拷贝。这种情况叫做集合的单层深复制 (One-Level-Deep Copy)。

测试NSDictionary/NSMutableDictionary:

NSDictionary *dict = @{@"key"  : @"value",
                       @"key1" : @"value1"};
NSMutableDictionary *dict1 = [dict mutableCopy];
NSLog(@"dict=%p, dict1=%p", dict, dict1);

for (id obj in dict.allValues) {
  NSLog(@"%p", obj);
}

for (id obj in dict1.allValues) {
  NSLog(@"%p", obj);
}

// 打印结果:
dict=0x7f9e70c3a0b0, dict1=0x7f9e70c2f100

0x1021f40a0
0x1021f40e0

0x1021f40a0
0x1021f40e0

从打印结果可以看出来这是NSArray/NSMutableArray的情况是一样的,因此对于NSSet/NSMutableSet自然也是一样的,它们都属于集合类型。

copy不一定是浅拷贝

我们来考虑NSString/NSMutableString之间的互相拷贝情况:

  1. NSString对象copy得到NSString对象

  2. NSMutableString对象copy得到NSString对象

NSString对象copy得到NSString对象:

NSString *str = @"标哥的技术博客";
// 因为NSString对象本身就不可变,所以并没产生新的对象,而是返回对象本身,但是引用计数加1
NSString *str2 = [str copy];
     
// 输出二者的地址,二者的地址是相同的
NSLog(@"str --> %p",str);
NSLog(@"str2 --> %p",str2);

// 打印结果:
str --> 0x10c820068
str2 --> 0x10c820068

从打印结果可以看到地址是一样的,因此它并没有做深拷贝,而是浅拷贝。

NSMutableString对象copy得到NSString对象:

NSMutableString *str = [[NSMutableString alloc] initWithString:@"标哥的技术博客"];
NSString *str2 = [str copy];
NSLog(@"str --> %p",str);
NSLog(@"str2 --> %p",str2);

// 打印结果:
str --> 0x7f9393e0fa90
str2 --> 0x7f9393e7cc10

从打印结果可以看到两个地址不一样,说明发生的是深拷贝。

自定义对象Copy/NSMutableCopy

对于自定义的对象,默认是不遵守NSCopying/NSMutableCopying协议的,如果我们希望自定义对象可以调用copy/mutableCopy,则需要手动去实现。

Copy需要遵守NSCopying协议,重写+copyWithZone

@interface Blog : NSObject<NSCopying>

@property (nonatomic, copy) NSString *name;
@property (nonatomic, copy) NSString *version;

@end


@implementation Blog

// 重写它
- (id)copyWithZone:(NSZone *)zone {
  Blog *blog = [[Blog alloc] init];
  blog.name = self.name;
  blog.version = self.version;
  
  return blog;
}

@end

测试来测试一下是否可以正常调用copy:

Blog *blog = [[Blog alloc] init];
blog.name = @"标哥的技术博客";
blog.version = @"v1.0.0";
  
Blog *anotherBlog = [blog copy];
NSLog(@"blog=%p, anotherBlog=%p", blog, anotherBlog);

// blog=0x7ffd13499f50, anotherBlog=0x7ffd1348f3a0

可以正常使用了!调用copy是生成新的对象!

NSMutableCopy需要遵守NSMutableCopying协议,重写+mutableCopyWithZone

似乎没有见过自定义的遵守NSMutableCopying协议的类型,因为对于可变操作通常是可以执行增、删、改、查操作的类型,最常见的自然是集合类型,还有字符串类型,所以对于自定义类型没有想出在哪些场景需要使用到,故暂不给出例子!

推荐阅读

还有另外一篇是iOS深拷贝与浅拷贝精讲,也是对深拷贝与浅拷贝之间的区别的探索性文章,推荐给大家!


承接:ThinkPHP项目开发、网站项目开发、微信项目开发、微信小程序项目开发、App开发,欢迎联系标哥QQ632840804