iOS基础 - copy 和 mutableCopy

在iOS中并不是所有的对象都支持copy,mutableCopy,遵守NSCopying/NSMutableCopying 协议的类可以发送copy/mutableCopy消息,否则就会发生异常。默认的iOS类并没有遵守这两个协议。如果想自定义一下copy/mutableCopy 那么就必须遵守NSCopying/NSMutableCopying,并且实现 copyWithZone: /mutableCopyWithZone:方法.

copy 和 mutableCopy分两种情况讨论,非容器类对象和容器类对象。需要有一个注意的点是,copy返回的对象是不可变的。

一 、对于非容器类对象:

这里指的是NSString,NSNumber等等一类的对象

  • retain:对象引用计数器自增+1,与原对象指向同一地址。结果和浅拷贝一样,
  • copy:
    • 对不可变对象 执行浅拷贝,等同与retain,指针拷贝,引用计数器+1(同上);
    • 对可变对象 执行深拷贝,等同mutableCopy,对象拷贝(同下)。
  • mutableCopy:
    • 对不可变对象 执行深拷贝,创建新的实例对象,分配了一块新的内存,拷贝原对象的值。
    • 对可变对象 执行深拷贝,创建新的实例对象,分配了一块新的内存,拷贝原对象的值。
实际验证一下,结果显而易见:
    
    //对非容器不可变对象copy
    NSString * str      = @"a";
    NSString * strCopy  = [str copy];
    NSLog(@"对非容器不可变对象copy");
    NSLog(@"str     %p",str);
    NSLog(@"strCopy %p",strCopy);
    
    //对非容器可变对象copy
    NSMutableString * mstr = [[NSMutableString alloc] init];
    mstr.string = @"b";
    NSMutableString * mstrCopy = [mstr copy];
    NSLog(@"对非容器可变对象copy");
    NSLog(@"mstr     %p",mstr);
    NSLog(@"mstrCopy %p",mstrCopy);
    
    //对非容器不可变对象mutablecopy
    NSString * strMutableCoy  = [str mutableCopy];
    NSLog(@"对非容器不可变对象mutablecopy");
    NSLog(@"str             %p",str);
    NSLog(@"strMutableCoy   %p",strMutableCoy);
    
    //对非容器可变对象mutablecopy
    NSMutableString * mstrMutableCopy = [mstr mutableCopy];
    NSLog(@"对非容器可变对象mutableCopy");
    NSLog(@"mstr            %p",mstr);
    NSLog(@"mstrMutableCopy %p",mstrMutableCopy);

非容器对象copy:mutablecopy.png

综上,对于非容器类对象,我们可以认为 :

  • 不可变对象复制,copy是浅拷贝(指针复制)和mutableCopy就是深拷贝(对象复制)。
  • 对可变对象复制,都是深拷贝。

二 、对于容器类对象:

这里指的是NSArray,NSDictionary等。对于容器类对象本身,上面的结论同样适用。
需要探讨的是复制后 容器内元素对象 的变化。

    //对容器 不可变对象 copy
    NSArray * arr      = [NSArray arrayWithObjects:@"a",@"b", nil];
    NSArray * arrCopy  = [arr copy];
    NSLog(@"对容器不可变对象copy");
    NSLog(@"arrarr.lastObject  %p",arr.lastObject);
    NSLog(@"arrCopy.lastObject %p",arrCopy.lastObject);
    
    //对容器 可变对象 copy
    NSMutableArray * mArr = [[NSMutableArray alloc] initWithObjects:@"a",@"b", nil];
    NSMutableArray * mArrCopy = [mArr copy];
    NSLog(@"对容器可变对象copy");
    NSLog(@"mArr.lastObject    %p",mArr.lastObject);
    NSLog(@"mArrCopy.lastObject%p",mArrCopy.lastObject);
    
    //对容器 不可变对象 mutablecopy
    NSArray * arrMutableCopy  = [arr mutableCopy];
    NSLog(@"对非容器不可变对象mutablecopy");
    NSLog(@"arr.lastObject              %p",arr.lastObject);
    NSLog(@"arrMutableCopy.lastObject   %p",arrMutableCopy.lastObject);
    
    //对容器 可变对象 mutablecopy
    NSMutableArray * mArrMutableCopy = [mArr mutableCopy];
    NSLog(@"对非容器可变对象mutableCopy");
    NSLog(@"mArr.lastObject             %p",mArr.lastObject);
    NSLog(@"mArrMutableCopy.lastObject  %p",mArrMutableCopy.lastObject);
容器对象copy:mutablecopy.png

综上,对于容器类对象,我们可以认为 :

  • 对于容器类对象本身,copy/mutablecopy 执行结果同非容器类对象。
  • 对于容器类对象 容器内的元素对象,都是浅拷贝。

如何实现容器内元素对象的深拷贝

两种方法

    NSArray * arr = [NSArray arrayWithObjects:[NSMutableString stringWithString:@"x"],@"a",@"b",[NSMutableString stringWithString:@"c"], nil];
    NSArray * arrDeepCopy=[[NSArray alloc] initWithArray: arr copyItems: YES];
    NSArray * arrTrueDeepCopy = [NSKeyedUnarchiver unarchiveObjectWithData:[NSKeyedArchiver archivedDataWithRootObject: arr]];
    
    NSLog(@"%p  %p  %p",arr.firstObject,            [arr objectAtIndex:1],              arr.lastObject);
    NSLog(@"%p  %p  %p",arrDeepCopy.firstObject,    [arrDeepCopy objectAtIndex:1],      arrDeepCopy.lastObject);
    NSLog(@"%p  %p  %p",arrTrueDeepCopy.firstObject,[arrTrueDeepCopy objectAtIndex:1],  arrTrueDeepCopy.lastObject);

元素对象深拷贝.jpg

综上可见:

  • arrTrueDeepCopy 使用 归档解档操作 是完全意义上的深拷贝,拷贝了元素对象本身;
  • arrDeepCopy 使用 copyItems 是官方文档定义的深拷贝方法。其实不是,arrDeepCopy内的不可变元素其实还是指针复制。原因如下。

因为如果容器的某一元素是不可变的,复制完后该对象仍旧是不能改变的,因此只需要指针复制即可。除非你对容器内的元素重新赋值,否则指针复制即已足够。

举个例子,[[arr objectAtIndex:0]appendstring:@”123”] 后其他的容器内对象并不会受影响(深拷贝的)。[[arr objectAtIndex:1] 和 [[arrDeepCopy objectAtIndex:1] 尽管是指向同一块内存地址,但是我们没有办法对其进行修改——因为它是不可改变的。所以指针复制已经足够。所以这并不是完全意义上的深拷贝,但是apple的官方文档将其列为deep copy了,并添加了copy和mutablity的关系说明,故在此做一说明。

receiver是不可变对象的时候,接收到copy消息时。只要达到retain的效果就可以了。这样一方面可以满足定义的要求,另一主面可以节省资源。这也是为什么copy和mutablecopy会有这样的实现。

具体应用

在修饰不可变的对象时,例如NSString 和NSArray 等等时,推荐使用copy 而非其他修饰,更有安全性。

相关参考链接 c杂谈之指针与数组

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

推荐阅读更多精彩内容