浅拷贝和深拷贝(iOS)

或许你在iOS开发的过程中很少用到copy or mutablecopy,但是有的时候阅读一些源码你会遇到,然后你草率的觉得自己懂了--在心里说so esay,但是当第二天的闹钟叫醒你去挤地铁时,你却什么也想不起来。废话说多了,这里并不是什么高超的技术难题需要我们攻克,只是理解记住。Just do it.
  copy就是我们理解的复制,拷贝一个对象去创建一个新的对象拥有原来对象的相同的类型相同的属性。什么时候我们会需要去copy一个对象呢?

NSMutableString *mutableTest1 = [[NSMutableString alloc]initWithString:@"我是可变字符串"];
NSString *test2 = mutableTest1;

这个时候如果我们用test2去做一些事情那么它就是不安全的,因为随时mutableTest1有可能修改他的值,但我们仅仅想用他的值做我们的事情,这个时候就需要我们的copy。

了解浅拷贝(Shallow-copy)和深拷贝(Deep-copy)

拷贝一个对象可以浅拷贝也可以深拷贝,他们的相同点是都是直接复制他们的属性,不同点是怎么处理指针的引用。深拷贝直接复制所引用的对象而浅拷贝则复制对象的引用。



  因此,如果对象A被浅拷贝到对象B,则对象B引用对象A所指向的相同的实例变量(或属性)。 深复制对象优先于浅复制,特别是对于值对象。深拷贝则新建一个对象,他们是不同的对象。拷贝同样会使引用计数+1,所以当你拷贝一个对象的时候有责任去释放他。

非集合对象的拷贝

顾名思义不是指集合类(数组,字典等)。首先一个对象能被拷贝需要遵循NSCopying协议并且实现了他的方法copyWithZone:,否则会在运行时的报错。如果是可变对象 遵循NSMutableCopying 协议并且实现 mutableCopyWithZone:来保证他的可变性。这时你可以利用copy和mutablecopy方法来进行复制操作,这两个方法会直接返回上面两个协议方法的返回值。

    NSString *test1 = @"woshibukebian";
    NSString *copytest1 = [test1 copy];
    NSMutableString *copytest2 = [test1 copy];
    NSMutableString *mutableCopytest1 = [test1 mutableCopy];
    NSLog(@"%p,%@",test1,test1);
    NSLog(@"%p,%@",copytest1,copytest1);
    NSLog(@"%p,%@",copytest2,copytest2);
    NSLog(@"%p,%@",mutableCopytest1,mutableCopytest1);
//    [copytest2 appendString:@"hahhahahh"]; //报错
    [mutableCopytest1 appendString:@"hahhahahh"];
    NSLog(@"%p,%@",mutableCopytest1,mutableCopytest1);
2017-01-21 13:56:03.980 CopyDemo[25182:1130625] test1,0x10a1bb068,woshibukebian
2017-01-21 13:56:03.980 CopyDemo[25182:1130625] copytest1,0x10a1bb068,woshibukebian
2017-01-21 13:56:03.980 CopyDemo[25182:1130625] copytest2,0x10a1bb068,woshibukebian
2017-01-21 13:56:03.980 CopyDemo[25182:1130625] mutableCopytest1,0x608000072700,woshibukebian
2017-01-21 13:56:03.981 CopyDemo[25182:1130625] mutableCopytest1,0x608000072700,woshibukebianhahhahahh

对于不可变的对象copy方法只是复制了对象的引用,copytest1复制了test1对象的引用,即copytest1和test1指向相同的对象保存了相同的指针,属于浅拷贝。不可变的对象mutableCopy方法,则复制了引用的对象,重新创建了内存来保存,并且对象拥有可变性。
不可变对象copy方法属于浅拷贝仅仅是指针复制,mutableCopy方法是深拷贝会重新创建一个内存复制引用的对象。

    NSMutableString *test1 = [[NSMutableString alloc]initWithString:@"我是可变字符串"];
    NSMutableString *copytest1 = [test1 copy];
    NSMutableString *mutableCopytest1 = [test1 mutableCopy];
    NSLog(@"test1,%p,%@",test1,test1);
    NSLog(@"copytest1,%p,%@",copytest1,copytest1);
    NSLog(@"copytest2,%p,%@",mutableCopytest1,mutableCopytest1);
    [copytest1 appendString:@"11111"]; //报错
    [mutableCopytest1 appendString:@"hahhahahh"];
    NSLog(@"mutableCopytest1,%p,%@",mutableCopytest1,mutableCopytest1);
2017-01-21 14:16:01.663 CopyDemo[25602:1139306] test1,0x60800007b440,我是可变字符串
2017-01-21 14:16:01.664 CopyDemo[25602:1139306] copytest1,0x608000050770,我是可变字符串
2017-01-21 14:16:01.664 CopyDemo[25602:1139306] mutableCopytest1,0x60800007b780,我是可变字符串
2017-01-21 14:16:01.664 CopyDemo[25602:1139306] mutableCopytest1,0x60800007b780,我是可变字符串hahhahahh

对于可变对象不论是copy还是mutableCopy方法都是深拷贝,都是重新创建了对象,复制的是引用的对象。copy的对象不可变,mutableCopy保持可变性。

总结:不可变对象因为本身是不可变的所以仅仅复制对象的引用即指向对象的指针就可以。copy复制了对象的指针,mutableCopy拷贝了引用的对象创建了可变对象保存原来引用的对象。

集合对象的拷贝

最常见的拷贝是浅拷贝,创建一个新的集合对象和原来的对象共享集合元素。深拷贝创建一个新的集合并把原来的集合的元素添加到新的集合中



这有一些方法去创建一个浅拷贝,当你创建一个浅拷贝原来的对象收到retain消息同时指向对象的指针被复制到新的集合来保存。

NSArray *shallowCopyArray = [someArray copyWithZone:nil];
NSDictionary *shallowCopyDict = [[NSDictionary alloc] initWithDictionary:someDictionary copyItems:NO];

我们可以利用copyWithZone:方法来拷贝一个集合或mutableCopyWithZone:或者initWithArray:copyItems: 方法。
  这里有两种方法来创建一个集合元素的深拷贝,你可以调用 initWithArray:copyItems:方法在第二个参数中传入YES。如果你调用这个方法来创建一个集合的深拷贝这时会向原来集合中的所有元素发送copyWithZone:方法。如果这个元素对象没有遵循NSCopying协议就会向我们前面描述的会在运行时报错,然而copyWithZone:仅仅会做浅拷贝。这种拷贝仅仅创建了一个一层深拷贝不是完全意义上的深拷贝( This kind of copy is only capable of producing a one-level-deep copy),仅仅复制了集合中元素的指针并不是完全意义上的深拷贝,如果集合元素是一个不可变对象这可以满足深拷贝,但如果是一个集合的集合那么集合元素同样有可能会发生变化。
  如果您需要真正意义上的深拷贝你可以利用归档然后解归档的方法来进行深拷贝。

NSArray* trueDeepCopyArray = [NSKeyedUnarchiver unarchiveObjectWithData:
[NSKeyedArchiver archivedDataWithRootObject:oldArray]];

但你拷贝一个集合,该集合和集合中的元素的可变性有可能会受到一些影响,不同的方法对应不同影响。

  • copyWithZone: 仅仅是表面的不可变,所有深层的可变性任然保留。
  • initWithArray:copyItems: with NO 赋予表面的可变性和原来创建的一样,但是深层的可变性任然保持。
  • initWithArray:copyItems: with YES 赋予表面的可变性和原来创建的一样,但是下一层的为不可变,所有的更深的层次保持原来的可变性。
  • 归档和解归档会保留原来的所有的可变性,因为创建了全新的对象保留了原来的数据与他的可变性。

希望到现在能对copy有了更深入的了解,当然我的文字功底不好或解释与理解有偏差,希望大家指出。祝大家技术节节高!
  努力的日子就是最美好的日子。——乔治亚·欧姬芙

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

推荐阅读更多精彩内容