iOS 底层 - 内存管理之copy

本文源自本人的学习记录整理与理解,其中参考阅读了部分优秀的博客和书籍,尽量以通俗简单的语句转述。引用到的地方如有遗漏或未能一一列举原文出处还望见谅与指出,另文章内容如有不妥之处还望指教,万分感谢 !

拷贝语法在开发中经常用到,拷贝的目的也很简单:产生一个新的副本对象,跟原对象互不影响;

  • 互不影响: 修改原对象不会影响副本对象,修改副本对象也不会影响原对象

在iOS中提供了两个拷贝方法:

  • copy:不可变拷贝,产生不可变副本
    不可变副本:不论拷贝对象是可变还是不可变,最终生成的都是不可变对象;比如:NSStringNSMutableString拷贝生成的都是NSString

  • mutableCopy : 可变拷贝,产生可变副本
    可变副本: 不论拷贝对象是可变还是不可变,最终生成的都是可变对象;
    比如:NSStringNSMutableString拷贝生成的都是NSMutableString

深拷贝、浅拷贝

深拷贝:内容拷贝,不可变产生可变、可变产生不可变、可变产生可变;会生成新对象

  • 调用copy时类型不同的才是深拷贝,调用mutableCopy时类型相不相同都是深拷贝
  • 产生的副本对象存储在堆空间,因其为可变对象所占空间大小不固定

浅拷贝:指针拷贝,不可变产生不可变 ,两个指针指向同一个内存地址;不会生成新对象

  • 产生的副本对象可能存储在栈空间、也可能存储在堆空间,因所占内存大小而定
  • 指针拷贝:不可变对象本身就不可被修改,于是为了节省资源占用,所以就让其指向同一个对象,只拷贝其指针即可;

在OC对象中只有以下是浅拷贝:
[NSString copy]
[NSArray copy]
[NSDictionary copy]

总结:可变和不可变有四种组合,只有不可变产生不可变是浅拷贝,其它都是深拷贝;所以深拷贝和浅拷贝的明显区别就是:是否产生新对象

注意不要混淆 copy、mutableCopy浅拷贝、深拷贝

  • copy和mutableCopy是两种不同的拷贝方式
  • 浅拷贝和深拷贝是对这两种方式产生的结果划分
  • 划分依据:是否产生新对象

图解

拷贝综合图解.png

用生活中的场景来生动的描述深拷贝和浅拷贝

浅拷贝

小明和小鱼俩是邻居,一天小明发现小鱼有一个可以看时间的电子表很羡慕,回到家嚷嚷着要让家人也给弄一个电子表看时间,小明爸爸就去挑了一个和小鱼一样的给小明看时间,小明很开心!

深拷贝

小明和小鱼俩是邻居,一天小明发现小鱼有一个可以看时间的电子表很羡慕,可恶的是小鱼竟然说他的手表时最好的;委屈回到家嚷嚷着要让家人也给弄一个比小鱼的电子表更好的电子表,小明爸爸马上到城里找到那家店把除了小鱼那款的其他三款更好的都打包带回家给小明玩 ,小明很开心!

示例代码

    NSString *str1 = [[NSString alloc] initWithFormat:@"test"];
    NSString *str2 = [str1 copy]; // 浅拷贝,指针拷贝,没有产生新对象
    NSMutableString *str3 = [str1 mutableCopy]; // 深拷贝,内容拷贝,有产生新对象
    
    NSLog(@"%p %p %p", str1, str2, str3);

    log输出
    //str1: 0x8b12089136018f1b
    //str2: 0x8b12089136018f1b
    //str3: 0x1005992d0
    

从log输出可以看出
str1和str2地址相同,说明指向同一个对象,没有产生新对象;
str3的地址变了,说明和str1指向的不是同一个对象,有产生新对象;

内存分析图.png

疑问:不是说修改副本对象不会影响原对象吗 ?str1和str2地址相同,都没有产生新对象呀 ?

  • str1和str2地址虽然相同,但依然产生了副本对象,只不过是副本对象和原对象相同而已
  • 原因:
    这是编译器优化的结果。编译器在编译时发现str1和str2的内容是相同常量字符串,会把它们都指向一个相同的区域,而不是再开辟出一块额外的空间。因此它们是相同的地址值。
    这样的拷贝被称为浅拷贝,浅拷贝的特点就是不会生成新对象,是指针拷贝

如果让 str2 指向另一个字符串呢 ?会影响str1吗

str2指向另一个字符串,相当于改变了指针指向,对str1的值没有影响;

如下代码


 NSString *str1 = [[NSString alloc] initWithFormat:@"test"];
    NSString *str2 = [str1 copy]; // 浅拷贝,指针拷贝,没有产生新对象
    NSMutableString *str3 = [str1 mutableCopy]; // 深拷贝,内容拷贝,有产生新对象
    
    NSLog(@"%p %p %p", str1, str2, str3);
    
    //str1: 0x8b12089136018f1b
    //str2: 0x8b12089136018f1b
    //str3: 0x1005992d0
    
    str2 = [[NSString alloc] initWithFormat:@"逍遥侯"];
    NSLog(@"%p %p", str1, str2);
    //str1: 0x8b12089136018f1b
    //str2: 0x10061f0a0
    

拷贝后产生的对象是不可变对象,这一定是浅拷贝吗 ?

  • 不一定,产生不可变对象的情况并不唯一;原对象类型是可变和不可变都有可能
  • 对一个可变对象拷贝和对一个不可变对象拷贝,产生的副本对象都是不可变对象;
  • 对一个可变对象拷贝产生不可变对象,这叫深拷贝;因为副本对象和原对象类型不同
  • 对一个不可变对象拷贝产生不可变对象,这叫浅拷贝;因为副本对象和原对象类型相同

拷贝后产生的对象是可变对象,这一定是深拷贝吗 ?

是的,因为只有调用mutableCopy才会产生可变副本对象,而mutableCopy返回的对象都是深拷贝

说的都是OC对象拷贝,那block能使用copy吗 ?如果可以是深拷贝还是浅拷贝 ?为什么

  • block可以被拷贝,block是指向结构体的指针,本质上是一个OC 对象,它的内部有个isa指针,和OC对象的底层结构相同;所以可以被拷贝

  • 是深拷贝,一个被声明的block首先会被存放在栈上,对其拷贝后编译器会自动将栈上的block拷贝到堆上;这过程中生成新的对象,是属于内容拷贝,所以是深拷贝

拷贝语法使用有哪些使用注意点 ?

  • @property声明属性时:

NSString 用copy修饰
NSArray 用copy修饰
NSDictionary 用copy修饰

NSMutableString 用strong修饰
NSMutableArray 用strong修饰
NSMutableDictionary 用strong修饰

  • 字符串用copy修饰的原因:
    可以保证不管外面传进来的是可变还是不可变,到我这里通通是不可变;这就确保了自身数据的独立,字符串通常是用来显示在屏幕上的,屏幕上显示的文字肯定不能相同,若想显示不同文字,就要保证相互不受影响;若外面想要改变显示出来的文字,需要再次赋值;不允许你改我就改,所以用copy最合适

  • 可变数组和可变字典不用copy修饰的原因:
    在数据传递场景下NSMutableStringNSMutableArrayNSMutableDictionary用 copy 后数据类型都发生变化了,这会造成很多潜在的问题,比如抛出异常方法找不到的错误

  • 一个类如果想使用copy,需要遵循NSCopying协议,并实现copyWithZone:方法

- (id)copyWithZone:(nullable NSZone *)zone;
  • 一个类如果想使用mutableCopy,需要遵循NSMutableCopying协议,并实现mutableCopyWithZone:方法
- (id)mutableCopyWithZone:(nullable NSZone *)zone;

为什么不能用mutableCopy修饰 ?

  • 在属性声明时不存在mutableCopy关键字的,官方并未提供;
  • 因为:属性声明的对象类型有很多种,mutableCopy是Foundation框架中只对某些特殊的类才可以使用,这些类都是遵循了拷贝协议(NSCopying、NSMutableCopying)的;所以不能用mutableCopy修饰

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

推荐阅读更多精彩内容