iOS 谈谈copy和mutableCopy

copy 是浅拷贝,mutableCopy 是深拷贝,从我学习的时候开始 老师貌似也是这么说的,我也一直是这么记的【尴尬】这是不对的!!!!!!!!!!

创建一个对象, 该对象与源对象的内容一致,此时使用copy与mutableCopy的区别:
copy拷贝出来的对象类型总是不可变类型(例如, NSString, NSDictionary, NSArray等等)
mutableCopy拷贝出来的对象类型总是可变类型(例如, NSMutableString, NSMutableDictionary, NSMutableArray等等)

声明属性的时候修饰使用copy修饰的原因:
补充一点堆栈的介绍:
堆和栈是操作系统的内存中堆和栈,不是数据结构中的堆和栈。
1、堆(heap)区:堆是由程序员分配和释放,用于存放进程运行中被动态分配的内存段,它大小并不固定,可动态扩张或缩减。当进程调用alloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用realse释放内存时,被释放的内存从堆中被剔除(堆被缩减),因为我们现在iOS基本都使用ARC来管理对象,所以不用我们程序员来管理,但是我们要知道这个对象存储的位置。
2、栈(stack)区:栈是由编译器自动分配并释放,用户存放程序临时创建的局部变量,存放函数的参数值,局部变量等。也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味这在数据段中存放变量)。除此以外在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也回被存放回栈中。由于栈的先进后出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上将我们可以把栈看成一个临时数据寄存、交换的内存区。

  • block为什么要用copy?
    MRC下不使用copy修饰, block存储在栈区, 作用域结束, block这个局部变量被销毁, 而一个strong指针还指向了这个被回收的地址.所以使用copy,把block拷贝到堆区, 并且指向堆区的block. 但是在ARC下无论copy,还是stong都无所谓, 因为系统已经在创建block的时候, 已经拷贝到堆区了。copy在MRC中是为了保护block的封装性将其移动至堆区。但在ARC中更多的是为了语义化、因为系统自动帮你移动了

补充个常用的NSString用copy修饰的原因:
用copy是为了安全,防止NSMutableString赋值给NSString时,前者修改引起后者值变化而用的.这个也适用于别的下文也会提到并验证。

@property (nonatomic,strong) NSString *myName;
NSMutableString *mStr = [[NSMutableString alloc] initWithString:@"daLi"];
    self.myName = mStr;
    NSLog(@"%@", self.myName);
    [mStr appendString:@"好美"];
    NSLog(@"%@", mStr);
    NSLog(@"%@", self.myName);
    /*
     分析:self.myName声明的时候使用strong修饰,会跟着mStr的改变而改变
     输出:
     2017-12-11 16:41:39.146126+0800 Demo[20438:1163039] daLi
     2017-12-11 16:41:39.146357+0800 Demo[20438:1163039] daLi好美
     2017-12-11 16:41:39.146496+0800 Demo[20438:1163039] daLi好美
     */
@property (nonatomic,copy) NSString *myName;
NSMutableString *mStr = [[NSMutableString alloc] initWithString:@"daLi"];
    self.myName = mStr;
    NSLog(@"%@", self.myName);
    [mStr appendString:@"好美"];
    NSLog(@"%@", mStr);
    NSLog(@"%@", self.myName);
    /*
     分析:self.myName声明的时候使用copy修饰,不会跟着mStr的改变而改变
     输出:
     2017-12-11 16:46:08.367018+0800 Demo[20507:1166962] daLi
     2017-12-11 16:46:08.367227+0800 Demo[20507:1166962] daLi好美
     2017-12-11 16:46:08.367353+0800 Demo[20507:1166962] daLi
     */

浅拷贝和深拷贝的分析
先介绍一下什么是iOS里面的系统容器类:
比如NSArray,NSMutableArray,NSDictionary,NSMutableDictionary....
浅拷贝和深拷贝?????英文就是shallow copy和deep copy

  • 浅拷贝:正常的拷贝,生成一个新的容器,但却是和原来的容器共用内部的元素;
  • 深拷贝:不仅生成新的容器,还生成了新的内部元素。

稍作总结:浅拷贝复制容器,深拷贝复制容器及其内部元素
(copy 与 mutableCopy 不等同于浅拷贝与深拷贝)
接下来用代码验证这些:

NSMutableArray *mArr = [NSMutableArray arrayWithObject:@1];
    NSLog(@"1-%@--%p", mArr, mArr);
    NSMutableArray *marr0 = [NSMutableArray arrayWithObject:mArr];
    NSLog(@"2-%@--%p--%p", marr0, marr0, marr0[0]);
    NSMutableArray *mCopyArr = [marr0 mutableCopy];//替换成copy是一样的效果
    NSLog(@"3-%@--%p--%p", mCopyArr, mCopyArr, mCopyArr[0]);
    [mCopyArr[0] addObject:@2];
    NSLog(@"4-%@--%p--%p", mCopyArr, mCopyArr, mCopyArr[0]);
    NSLog(@"5-%@--%p--%p", marr0, marr0, marr0[0]);
    [marr0[0] addObject:@3];
    NSLog(@"6-%@--%p--%p", mCopyArr, mCopyArr, mCopyArr[0]);
    NSLog(@"7-%@--%p--%p", marr0, marr0, marr0[0]);
/*
 修改了mCopyArr的元素,marr0也跟着改变了元素,因为二者的元素是共用的,[marr0 mutableCopy]只拷贝了数组容器,里面的元素并没有拷贝,而是和marr0共用的元素,所以marr0[0]、mCopyArr[0]元素的地址是一个,无论修改它们俩哪一个,对应的数组marr0、mCopyArr里面的元素都会跟着改变。
 由此验证出来:容器类的mutableCopy和copy是浅拷贝,只拷贝容器,并不拷贝元素!!
 输出结果:
 2017-12-11 14:41:53.551658+0800 Demo[19619:1091522] 1-(
 1
 )--0x600000059f50
 2017-12-11 14:41:53.551876+0800 Demo[19619:1091522] 2-(
 (
 1
 )
 )--0x6000002576a0--0x600000059f50
 2017-12-11 14:41:53.552080+0800 Demo[19619:1091522] 3-(
 (
 1
 )
 )--0x60000000b9f0--0x600000059f50
 2017-12-11 14:41:53.552357+0800 Demo[19619:1091522] 4-(
 (
 1,
 2
 )
 )--0x60000000b9f0--0x600000059f50
 2017-12-11 14:41:53.552545+0800 Demo[19619:1091522] 5-(
 (
 1,
 2
 )
 )--0x6000002576a0--0x600000059f50
 2017-12-11 14:41:53.552687+0800 Demo[19619:1091522] 6-(
 (
 1,
 2,
 3
 )
 )--0x60000000b9f0--0x600000059f50
 2017-12-11 14:41:53.552806+0800 Demo[19619:1091522] 7-(
 (
 1,
 2,
 3
 )
 )--0x6000002576a0--0x600000059f50
 */

写一个NSArray验证一下

NSArray *arr = @[@"1"];
    NSLog(@"1-%@--%p", arr, arr);
    NSArray *arr0 = arr;
    NSLog(@"2-%@--%p", arr0, arr0);
    NSArray *arr1 = [arr copy];
    NSLog(@"3-%@--%p", arr1, arr1);
    NSArray *arr2 = [arr mutableCopy];
    NSLog(@"4-%@--%p", arr2, arr2);
    arr = @[@"2"];
    NSLog(@"5-%@--%p", arr, arr);
    NSLog(@"6-%@--%p", arr0, arr0);
    NSLog(@"7-%@--%p", arr1, arr1);
    NSLog(@"8-%@--%p", arr2, arr2);
    /*
     分析:
     arr初始化指向的是@[@"1"]数组,
     1、arr0是直接用arr赋值的,都是指向了@[@"1"],当arr = @[@"2"],arr指向了@[@"2"]这个数组,而arr0不变,因为arr0依旧指向的是@[@"1"];
     2、arr1,[arr copy],使得arr1指向了arr指向的@[@"1"],后面arr的指向改变并不会导致arr1的指向改变;
     3、arr2,[arr mutableCopy],使得arr1指向了arr指向的@[@"1"],后面arr的指向改变并不会导致arr1的指向改变;
     输出:
     2017-12-11 15:12:21.130514+0800 Demo[19697:1107933] 1-(
     1
     )--0x600000001c20
     2017-12-11 15:12:21.130690+0800 Demo[19697:1107933] 2-(
     1
     )--0x600000001c20
     2017-12-11 15:12:21.130835+0800 Demo[19697:1107933] 3-(
     1
     )--0x600000001c20
     2017-12-11 15:12:21.130994+0800 Demo[19697:1107933] 4-(
     1
     )--0x604000056770
     2017-12-11 15:12:21.131122+0800 Demo[19697:1107933] 5-(
     2
     )--0x604000002380
     2017-12-11 15:12:21.131255+0800 Demo[19697:1107933] 6-(
     1
     )--0x600000001c20
     2017-12-11 15:12:21.131342+0800 Demo[19697:1107933] 7-(
     1
     )--0x600000001c20
     2017-12-11 15:12:21.131431+0800 Demo[19697:1107933] 8-(
     1
     )--0x604000056770
     */

cpoy 与直接赋值的对比分析

直接赋值:
    NSMutableArray *mArr = [NSMutableArray arrayWithObjects:@"1", nil];
    NSLog(@"0-%@--%p", mArr, mArr);
    NSArray *arr = mArr;
    NSLog(@"1-%@--%p", arr, arr);
    [mArr addObject:@"2"];
    NSLog(@"2-%@--%p", mArr, mArr);
    NSLog(@"3-%@--%p", arr, arr);
/*
 分析:使用直接赋值的方式给arr=mArr,可变数组mArr添加对象是在赋值之后, 为什么后面添加对象还会影响到不可变数组arr呢?? 原因很简单, 因为Objective-C支持多态. 所以表面上arr是NSArray对象, 其实骨子里是NSMutableArray对象.这样的话将会对后期DEBUG增加很大的成本, 可能会导致莫名其妙的错误.开发过程中应该注意此类情况
 输出:
 2017-12-11 15:46:21.532252+0800 Demo[20106:1129830] 0-(
 1
 )--0x604000441350
 2017-12-11 15:46:21.532407+0800 Demo[20106:1129830] 1-(
 1
 )--0x604000441350
 2017-12-11 15:46:21.532528+0800 Demo[20106:1129830] 2-(
 1,
 2
 )--0x604000441350
 2017-12-11 15:46:21.532652+0800 Demo[20106:1129830] 3-(
 1,
 2
 )--0x604000441350
 */
copy/mutableCopy:
NSMutableArray *mArr = [NSMutableArray arrayWithObjects:@"1", nil];
    NSLog(@"0-%@--%p", mArr, mArr);
    NSArray *arr = [mArr copy];//mutableCopy
    NSLog(@"1-%@--%p", arr, arr);
    [mArr addObject:@"2"];
    NSLog(@"2-%@--%p", mArr, mArr);
    NSLog(@"3-%@--%p", arr, arr);
/*
 分析:arr使用copy获取之后,再次修改mArr不会造成arr的改变,保障了arr的安全。
 输出:
 2017-12-11 15:51:29.749360+0800 Demo[20137:1133165] 0-(
 1
 )--0x60000005daf0
 2017-12-11 15:51:29.749537+0800 Demo[20137:1133165] 1-(
 1
 )--0x600000007ec0
 2017-12-11 15:51:29.749639+0800 Demo[20137:1133165] 2-(
 1,
 2
 )--0x60000005daf0
 2017-12-11 15:51:29.749787+0800 Demo[20137:1133165] 3-(
 1
 )--0x600000007ec0
 */

然后再写一个字典的来验证验证:

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

推荐阅读更多精彩内容

  • 前言 不敢说覆盖OC中所有copy的知识点,但最起码是目前最全的最新的一篇关于 copy的技术文档了。后续发现有新...
    zyydeveloper阅读 3,350评论 4 35
  • 本文为转载: 作者:zyydeveloper 链接:http://www.jianshu.com/p/5f776a...
    Buddha_like阅读 868评论 0 2
  • *面试心声:其实这些题本人都没怎么背,但是在上海 两周半 面了大约10家 收到差不多3个offer,总结起来就是把...
    Dove_iOS阅读 27,135评论 30 470
  • 设计模式是什么? 你知道哪些设计模式,并简要叙述?设计模式是一种编码经验,就是用比较成熟的逻辑去处理某一种类型的事...
    irenb阅读 5,200评论 1 21
  • 仰观历史星河,李白这位大诗人恐怕是浩瀚的诗词星座中最为明亮的星了。一首《静夜思》让彼时尚在懵懂年纪的我记住了这位诗...
    刘皮皮有点皮阅读 1,568评论 4 35