iOS 关于深拷贝和浅拷贝的误解

在iOS开发中深拷贝和浅拷贝是一个被大家说烂的话题了,但是今天还是要拿出来说一说。原因是,前段时间在微信朋友圈看到一个朋友发的关于深拷贝和浅拷贝的总结,当时看了一眼,我想他对深拷贝和浅拷贝没有理解,而且被网上很多关于这方面的技术博客误导了,我搜索了一下,发现网上很多的博客跟他下面总结的一样。而且他去面试时,很多面试官也是这么理解深拷贝和浅拷贝的。
他的总结如下图,大家可以看看,想想自己是否也是这么理解深拷贝和浅拷贝的:


copy_00.JPG

下面让我们先来看几个个例子,如果你认可上图的总结,那么看完下面的例子之后,请先想好结果,然后再对照答案,看看自己的结果是否正确。

1. NSMutableArray对象的拷贝

我们先来看看对可变数组NSMutableArray对象进行拷贝的例子:

        //1.创建四个可变字符串
        NSMutableString *av_0 = [NSMutableString stringWithString:@"Harry"];
        NSMutableString *av_1 = [NSMutableString stringWithString:@"Jamie"];
        NSMutableString *av_2 = [NSMutableString stringWithString:@"Micheal"];
        NSMutableString *av_3 = [NSMutableString stringWithString:@"Susan"];
        
        //2.创建一个可变数组,用来存放上面四个可变的字符串
        NSMutableArray *array_0 = [NSMutableArray arrayWithObjects:av_0,av_1, nil];
        
        //3.我们调用方法 mutableCopy 和 copy 来拷贝数组 array_0
        NSMutableArray *array_1 = [array_0 mutableCopy];
        NSArray *array_2 = [array_0 copy];
        
        
        //4. 修改可变字符串 av_0
        [av_0 appendString:@" Muhammed"];
  
        
        //5. 最后我们输出 array_0,array_1,array_2 中的元素,请先思考结果如何是什么
        NSLog(@"array_0 = %@ \n array_1 = %@\n array_2 = %@",array_0,array_1,array_2);
    

首先我们先按照图copy_00.JPG中关于NSMutableArray的总结来分析下结果,根据上图我们无论对NSMutableArray调用mutableCopy,还是copy方法,都是深拷贝。因此,当我们对数组array_0调用拷贝方法之后,同时也会对其中的两个元素av_0av_1进行拷贝。那么当我们改变av_0之后,对数组array_1array_2中的元素av_0应该是没有影响的,array_1array_2的输出仍然是Harry。但是实际结果真的如此吗?让我们看看最后的输出结果:

array_0 = (
    "Harry Muhammed",
    Jamie
) 
 array_1 = (
    "Harry Muhammed",
    Jamie
)
 array_2 = (
    "Harry Muhammed",
    Jamie
)

接着我们再向数组array_0array_1中分别添加一个元素"av_2",av_3。看看三个数组array_0array_1array_2中的元素个数是如何变化的,它们之间是否会彼此影响:

        [array_0 addObject:av_2];
        [array_1 addObject:av_3];
        
        //请思考 array_0,array_1,array_2 的输出是什么?
        NSLog(@"array_0 = %@ \n array_1 = %@\n array_2 = %@",array_0,array_1,array_2);

其输出结果如下:

array_0 = (
    "Harry Muhammed",
    Jamie,
    Micheal
) 
 array_1 = (
    "Harry Muhammed",
    Jamie,
    Susan
)
 array_2 = (
    "Harry Muhammed",
    Jamie
)

由上面的结果我们可以看出,当我们向可变数组array_0array_1中添加元素后,对数组array_02中的元素个数并没有影响,并且array_0array_1也没彼此影响。

         //创建一个不可变数组 array_3
        NSArray *array_3 = @[av_2,av_3];
        
        //拷贝数组 array_3 分别赋值给 array_4,array_5
        NSMutableArray *array_4 = [array_3 mutableCopy];
        NSArray *array_5 = [array_3 copy];

        //修改数组 array_3 中的元素 av_2
        [av_2 appendString:@" Jackson"];

        //请思考 array_3,array_4,array_5 中的元素是什么?
        NSLog(@"array_3 = %@ \n array_4 = %@\n array_5 = %@",array_3,array_4,array_5);
array_3 = (
    "Micheal Jackson",
    "Susan Boyle"
) 
 array_4 = (
    "Micheal Jackson",
    "Susan Boyle"
)
 array_5 = (
    "Micheal Jackson",
    "Susan Boyle"
)


        //创建数组 array_6,array_7
        NSMutableArray *array_6 = [[NSMutableArray alloc] initWithArray:array_3 copyItems:NO];
        NSMutableArray *array_7 = [[NSMutableArray alloc] initWithArray:array_3 copyItems:YES];
         [av_3 appendString:@" Boyle"];
        
        //请思考 array_6,array_7中的元素是什么?
        NSLog(@"array_6 = %@ \n array_7 = %@",array_6,array_7);
        
array_6 = (
    "Micheal Jackson",
    "Susan Boyle"
) 
 array_7 = (
    "Micheal Jackson",
    Susan
)

2. NSArray对象的拷贝

下面让我们再来看看对不可变数组NSArray对象进行拷贝的例子:

        //1.创建四个可变字符串
        NSMutableString *av_0 = [NSMutableString stringWithString:@"Harry"];
        NSMutableString *av_1 = [NSMutableString stringWithString:@"Jamie"];
        NSMutableString *av_2 = [NSMutableString stringWithString:@"Micheal"];
        NSMutableString *av_3 = [NSMutableString stringWithString:@"Susan"];

        //2.创建一个不可变数组 array_3
        NSArray *array_3 = @[av_2,av_3];

        //3.拷贝 array_3  分别赋值给 array_4,array_5
        NSMutableArray *array_4 = [array_3 mutableCopy];
        NSArray *array_5 = [array_3 copy];
        NSArray *array_6 = array_3;

        //4.修改数组 array_3 中的元素 av_2
        [av_2 appendString:@" Jackson"];

        //5.输出数组 array_3, array_4, array_5, array_6 中的元素,请先思考结果如何
        NSLog(@"array_3 = %@ \n array_4 = %@\n array_5 = %@ \n array_6 = %@",array_3,array_4,array_5,array_6);

现在我们也按照图copy_00.JPG中关于NSArray的总结来分析结果。当我们调用copy方法对不可变数组NSArray的对象进行拷贝时,是浅拷贝;而调用mutableCopy是深拷贝。因此,当我们通过调用copy方法对array_3进行拷贝,并赋值给array_5 ,此时array_3array_5都指向堆中的同一块内存空间;而通过调用mutableCopy方法拷贝的结果array_4array_3指向堆中不同的内存空间。那么当我们改变av_2时,array_3array_5中的av_2会变化,变成Micheal Jackson;而array_4中的av_2会不变化,仍然是Micheal。但实际结果真会如此吗,让我们一起来见证奇迹:

array_3 = (
    "Micheal Jackson",
    Susan
) 
 array_4 = (
    "Micheal Jackson",
    Susan
)
 array_5 = (
    "Micheal Jackson",
    Susan
) 
 array_6 = (
    "Micheal Jackson",
    Susan
)

我们重新创建一个NSArray对象,并赋值给array_3,并看看array_3array_4array_5array_6的输出结果,代码及运行结果如下:

   array_3 = [NSArray arrayWithObjects:av_0,av_1, nil];
   NSLog(@"array_3 = %@ \n array_4 = %@\n array_5 = %@ \n array_6 = %@",array_3,array_4,array_5,array_6);
array_3 = (
    Harry,
    Jamie
) 
 array_4 = (
    "Micheal Jackson",
    Susan
)
 array_5 = (
    "Micheal Jackson",
    Susan
) 
 array_6 = (
    "Micheal Jackson",
    Susan
)

4.关于 copy 和 mutableCopy

通过上面的两个例子我们可看出,对NSArrayNSMutableArray对象无论通过copy方法,还是mutableCopy方法进行拷贝,都是浅拷贝。我们只拷贝了容器对象本身,并不复制其中的数据。而copymutableCopy拷贝的结果 是可变对象和不可变对象。无论当前实例是否可变,若需要获取其可变版本的拷贝,均应该调用mutableCopy方法。同理,若需要不可变的拷贝,则均应该调用copy方法获取。
在Foundation框架中的所有collection类在默认情况下都执行浅拷贝,也就是说只拷贝容器对象本身,而不复制其中的数据。这样做的原因在于,容器内的对像未必都能拷贝(即遵守copy协议),而调用者也未必想在拷贝容器时一并拷贝其中的对象。

5. NSArray 和 NSMutableArray 的深拷贝

通过上面的分析我们知道,对数组对象通过copymutableCopy方法进行拷贝,都是浅拷贝。若我们想对数组进行深拷贝,可以调用该类的初始化方法- (instancetype)initWithArray:(NSArray<ObjectType> *)array copyItems:(BOOL)flag;来执行深拷贝。调用该方法的前提是,数组中的每个元素必须遵守copy 协议。

NSArray *array_7 = @[av_0,av_1];
NSArray *array_8 = [[NSArray alloc] initWithArray:array_7 copyItems:NO];
NSArray *array_9 = [[NSArray alloc] initWithArray:array_7 copyItems:YES];
[av_0 appendString:@" Potter"];
NSLog(@"array_7 = %@ \n array_8 = %@ \n array_9 = %@ ",array_7,array_8,array_9);

flag参数设为YES,则会向数组中的每个元素发送copy消息,用拷贝好的元素创建新的数组,并将其返回给调用者。当改变av_0时,array_9中的av_0仍然是Harry,而array_7array_8中的av_0则变成 Harry Potter。输出结果如下:

array_7 = (
    "Harry Potter",
    Jamie
) 
 array_8 = (
    "Harry Potter",
    Jamie
) 
 array_9 = (
    Harry,
    Jamie
)

对于Foundation框架中的所有collection(字典,数组,集合)类都提供了深拷贝的初始化方法。但是对于自定义类,因为没有专门的深拷贝协议,所以其具体的执行方式由每个类来确定,我们只需决定自己所写的类是否要提供深拷贝的方法即可。此外,遵守了copy 协议的对象不一定都会执行深拷贝。在大部分情况下,执行的都是浅拷贝。如果需要在某对象上执行深拷贝,除非该文档说明它是用深拷贝来实现copy 协议的,否则要么寻找能够执行深拷贝的相关方法,要么自己编写方法实现深拷贝。

6.深拷贝和浅拷贝

用一个与实例对象相同的内容,生成一个新的对象,这个过程就是复制。其中只复制对象的指针成为浅拷贝,而复制具有新的内存空间的对象成为深拷贝

图A.png

图B.jpg

图C.png

由上图可以看出,变量A指向了一个对象,而这个对象的实例变量又指向了另一个对象。把变量A复制到变量B时,图A只复制对象的指针,将对象代入到变量中;图B将变量A指向的对象原样复制一份,并通过指针来共享这个对象和复制体中的实例变量;图C复制对象中的实例变量,并递归的进行对象复制。
下图是苹果官方文档关于深拷贝和浅拷贝的说明示例图:
图 D.png

7.Swift 4.2 专题模块

Swift 4.2 基础专题详解

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容