Copy和mutableCopy实际使用差别分析

最近一直会遇到关于copy和mutableCopy相关的问题,之前在学习内存管理方面有大致的了解过,但是通过不断的了解发现存在特别多的疑惑点,所以趁着年前的时间将这方面的知识点总结总结。


1.类对象的Copy和MutableCopy

两者在字面上最直观的差别就是是:

copy复制出不可变的对象,而mutableCopy复制出可变的对象。

乍看上去很好理解,但是就着以下的几个方向思考会发现疑点重重:

① 复制的对象与原对象的关系

按理而言,复制应该就是申请了一块新的内存空间,和原对象只是内容相同。但是实际上并没有这么简单,主要通过非集合类和集合类来分别验证:

非集合类

以NSString为例

    NSString *str_1 = @"李周笔记";
    NSString * str_2 = [str_1 copy];
    NSString * str_3 = [str_1 mutableCopy];
    NSMutableString *str_4 = [str_1 copy];
    NSMutableString *str_5 = [str_1 mutableCopy];
    NSLog(@"%p , %p, %p, %p, %p",str_1,str_2,str_3,str_4,str_5);

运行之后,在断点处发发现以上所有创建的字符串对象信息:


字符串对象信息

发现str_4即便声明为MutableString可变类型,但是isa指针仍然指向的还是一个不可变的对象类型。而str_3虽然声明为了NSString不可变类型,但是isa指针却指向了一个可变的对象类型。

此处,isa指针主要的作用是找到该对象能执行的所有方法

简单的说,str_4调用MutableString相关方法时,可以通过编译却无法成功运行。

[str_4 appendString:@"非常好"];

运行之后直接崩溃报错:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with appendString:'

最后当打印所有对象的地址时,copy之后的对象指向的地址和str_1指向的内存地址一样。

0x10282ef28 , 0x10282ef28, 0x600000447350, 0x10282ef28, 0x600000448100

以NSMutableString为例:

    NSMutableString *str_1 = [NSMutableString stringWithString:@"李周笔记"];
    NSString * str_2 = [str_1 copy];
    NSString * str_3 = [str_1 mutableCopy];
    NSMutableString *str_4 = [str_1 copy];
    NSMutableString *str_5 = [str_1 mutableCopy];
    NSLog(@"%p , %p, %p, %p, %p",str_1,str_2,str_3,str_4,str_5);

在运行之后,断点查看控制台所有字符串的相关信息,发现所有的对象都是可变类型:


字符串对象信息

所以,对于Copy和mutableCopy的理解应该是:

Copy复制出和原对象相同类型的对象,mutableCopy复制出一个可变类型的对象。

以上所有字符串的指向内存地址都不相同,也就是互不影响:

0x60400044dda0 , 0x60400022c380, 0x60400044dd40, 0x60400022e700, 0x60400044dce0
集合类

以NSArray为例:

① 元素为非集合类型
    NSMutableString *str_1 = [NSMutableString stringWithString: @"李周"];
    NSMutableString *str_2 = [NSMutableString stringWithString:@"谢华华"];
    NSArray *arr_1 = [NSArray arrayWithObjects:str_1,str_2, nil];
    NSArray *arr_2 = [arr_1 copy];
    NSArray *arr_3 = [arr_1 mutableCopy];
    NSMutableArray *arr_4 = [arr_1 copy];
    NSMutableArray *arr_5 = [arr_1 mutableCopy];

打印所有数组对象的内存地址和数组第一个元素指向的内存地址的结果:

arr_1 ::0x6040002321a0, arr_1[0] ::0x604000249de0
arr_2 ::0x6040002321a0, arr_2[0] ::0x604000249de0
arr_3 ::0x604000241fb0, arr_3[0] ::0x604000249de0
arr_4 ::0x6040002321a0, arr_4[0] ::0x604000249de0
arr_5 ::0x60400024cc90, arr_5[0] ::0x604000249de0

使用copy生成的对象如arr_2和原NSArray对象指向的内存地址相同,但是arr_2和arr_4都是不可变类型,所以在数组层面上无法互相影响。
但是在元素层面上而言,数组的所有的第一个元素指向相同的内存地址,也就是说元素间互相影响.

    [str_1 appendString:@"周"];
    [arr_5[0] appendString:@"xxxx"];
    [arr_1[0] appendString:@"yyyyy"];

无论是以上面哪种方式操作,都是改变同一片内存区域中的内容,所以造成是改变了所有数组中的第一个元素。

② 元素为集合类型或自定义类对象
    User *user_1 = [[User alloc] initWithName:@"李周"];
    User *user_2 = [[User alloc] initWithName:@"谢华华"];
    
    NSArray *arr_1 = [NSArray arrayWithObjects:user_1,user_2, nil];
    NSArray *arr_2 = [arr_1 copy];
    NSArray *arr_3 = [arr_1 mutableCopy];
    NSMutableArray *arr_4 = [arr_1 copy];
    NSMutableArray *arr_5 = [arr_1 mutableCopy];
    
    NSLog(@" %p, %p, %p, %p, %p",arr_1,arr_2,arr_3,arr_4,arr_5);
   

所有数组对象的打印结果为:

0x600000029ee0, 0x600000029ee0, 0x600000252ff0, 0x600000029ee0, 0x600000252db0

使用copy创建的对象如arr_2和原对象arr_1的指向的内存地址相同。并且通过断点可知:


所有数组对象信息

无论使用Copy还是mutableCopy,所有的数组对象中的元素指向的内存地址都是一样的,也就是说如果改变其中一个数组中的元素,其他对象也会同时改变。
如改变arr_2中第一个元素的name属性

    User * user = (User *)arr_2[0];
    user.name = @"李周周";

因为所有数组的第一个元素都指向同一个user_1的内存地址,所以所有数组的第一个元素都会改变。

以上的两个例子涉及到了有关于深拷贝和浅拷贝:

浅拷贝,只是对指针的拷贝,拷贝后两个指针指向同一个内存空间。
深拷贝,不但是对指针的拷贝,而且对指针指向的内容进行拷贝。

简单而言,深拷贝的对象已经和原对象完全没有任何关系了。
上面对NSArray的拷贝就是浅拷贝,拷贝的对象中的元素与原对象中的元素指向同一个内存地址。如果想要变为深拷贝,可以将上面的代码改为:

    User *user_1 = [[User alloc] initWithName:@"李周"];
    User *user_2 = [[User alloc] initWithName:@"谢华华"];
    NSMutableArray *arr_1 = [NSMutableArray arrayWithObjects:user_1,user_2, nil];
    NSArray *arr_2 = [[NSArray alloc] initWithArray:arr_1 copyItems:YES];
    NSMutableArray *arr_5 = [[NSMutableArray alloc] initWithArray:arr_1 copyItems:YES];
    NSLog(@" %p, %p, %p",arr_1,arr_2,arr_5);
    
    user_1.name = @"李周周";

如果直接运行的话,会发现程序直接崩溃:

*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[User copyWithZone:]: unrecognized selector sent to instance 0x60400020ace0'

深拷贝对内容进行拷贝的关键就是调用相应类的copyWithZone:方法:

@interface User : NSObject<NSCopying>

-(id)copyWithZone:(NSZone *)zone
{
    User *user = [[User alloc] initWithName:_name];
    
    return user;
}

总结而言的话,只要理解了复制的到底是“对本身层面上的复制”还是“对本身层面之下的复制”,就能很好的在实际的开发中避免因为找不到相应的方法而崩溃的情况了。

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

推荐阅读更多精彩内容

  • 本文为转载: 作者:zyydeveloper 链接:http://www.jianshu.com/p/5f776a...
    Buddha_like阅读 824评论 0 2
  • 前言 不敢说覆盖OC中所有copy的知识点,但最起码是目前最全的最新的一篇关于 copy的技术文档了。后续发现有新...
    zyydeveloper阅读 3,252评论 4 35
  • 其实我一直对于例如属性中的copy OR [array copy]这样的使用稀里糊涂的。之前有总结过,无奈现在又忘...
    升级打怪啊怪阅读 1,150评论 0 3
  • 设计模式是什么? 你知道哪些设计模式,并简要叙述?设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类型的事...
    irenb阅读 5,123评论 1 20
  • 这天,新上任的县长到小吃摊吃早餐,刚找个板凳坐下,就听炸油条的胡老头一边忙活一边唠叨:“大家吃好喝好哦,城管要来撵...
    大鹏怀山药阅读 3,682评论 0 0