004 copy/mutableCopy 与 浅拷贝/深拷贝

“为学日益,为道日损,损之又损,以至于无为,无为而无不为”。用自己的思考去理解问题,从问题的源头探索,才有可能寻找到最接近设计者思想的答案。网上有非常多的博客,博客内容良莠不齐,且绝大多数是为被大众证实,仅仅是一面之词。日常阅读,日常解惑,有这两篇文档可以阅读,分别是更新在MRR、ARC时代的两篇文章。

Updated: 2009-10-21
Memory Management Programming Guide for Core Foundation
Updated: 2012-07-17
Advanced Memory Management Programming Guide

下面只是笔者对上面两篇文章一点读后感。

引用拷贝(Reference Copy)

Copy Functions

引用拷贝这个名词在文章中并没有直接指出,但有相关的概念提示,例如截图第二段中,第三行:copies the reference to the object,第五行中提到:the reference is duplicated,拷贝的结果是一个新的指针,故亦称之为指针拷贝

基本数据类型

下面以整数的拷贝为例:
myInt2 = myInt1

  1. myInt1myInt2 分别是两个独立的内存空间;
  2. myInt1 的值拷贝进 myInt2

Core Foundation类型

myCFString2 = myCFString1

  1. myCFString2myCFString1 都是CFStringRef类型,仅仅只是拷贝对象的引用,并不能拷贝对象的内容;
  2. 拷贝一个对象的类型(引用)是非常快的,因为没有拷贝对象的内容;
  3. 拷贝一个可变对象的的引用是非常危险的操作。因为在某个地方修改了这个对象的数据,其他地方的引用是无法知道数据已经被修改了;(数据的修改可以是对象本身被修改,也可以是对象的某个属性被修改)
  4. CFStringCreateCopy拷贝一个全新的对象,对象内容与原始数据一模一样,当我们在修改这个新对象时,是不会对旧对象造成任何影响;

浅拷贝(Shallow Copy)

Shallow Copy

上面提到了 CFStringCreateCopy,这个方法仅仅是适用于字符串的copy,而对于array,同样有CFArrayCreateCopy方法。CFxxtypexxCreateCopy系列的方法都会拷贝一个新的对象。

单一对象

例如CFStringRef、CFDataRef,在使用CFxxtypexxCreateCopy时,都会拷贝一个全新的对象。

容器对象

  1. 对容器对象本身,拷贝一个全新的容器对象;
  2. 新容器中的元素,只是拷贝的元素的引用;
  3. 文章给出的解释是,你只希望有一个不可变数组去记录这些元素,也就是你本身就不想去改变他们,那就没有必要分配额外的空间。

深拷贝(Deep Copy)

Deep Copy
  1. 深拷贝可以拷贝出一个全新的容器对象,对象本身以及容器对象里面的元素都会被拷贝;
  2. CFPropertyListCreateDeepCopy,对于给定的list,递归调用CFxxtypexxCreateCopy方法,拷贝容器中的元素;
  3. 如果需要深拷贝其他数据结构,需要自己手动递归拷贝容器对象里面的所有元素;
  4. 文章也指出,在递归的时候需要避免递归循环

从上面的一些特点不难分析出,深拷贝是一种概念,而不是某个具体的操作。不管是集合对象,还是自定义的实例对象,除了对象本身,他们还依赖一些其他对象,我们可以通过引用的方式来表示其他对象。当对象本身被拷贝时,仅仅只会开辟这个对象需要的空间,然后将原对象中的数据一比一复制一份存到这块内存,到这里就是浅拷贝的过程。如果是深拷贝,还需要把所有引用指向的对象再拷贝一份,让引用也指向新的对象,这样才能将这个对象以及这个对象所依赖的其他对象完全与原来的那份隔离开。
满足以下两个过程的拷贝可以称作深拷贝:

  1. 拷贝对象本身;
  2. 递归拷贝对象所引用的对象;

copy 和 mutableCopy

上面仅仅是对平时一直遇到,但没有理解透彻的概念做了一个整理,加深对这些概念的理解。上面的三个拷贝都是在MRR模式下,Core Foundation框架中的概念,而且有API支持。在ARC模式下,我们遇到最多的其实是strong/copy/mutableCopy这些关键词。

object graphs
  1. retain/strong,引用拷贝,对象的引用计数+1。图中classB对对象retain,引用计数变为2;
  2. copy,对象拷贝,会拷贝一个新的对象。图中classC对原对象copy,拷贝一个新的对象,新对象引用计数为1,图中虚线流程;

这张图可以佐证上面的一些概念。Foundation框架中的copy只表示一件事——拷贝对象,所以copy浅拷贝、深拷贝的概念无直接联系。在ARC中的copy可以理解为MRR中的CreateCopy——The Create Rule

mutableCopy

/* from CFBag.h */
CF_EXPORT CFBagRef  CFBagCreate(CFAllocatorRef allocator, const void **values, CFIndex numValues, const CFBagCallBacks *callBacks);
CF_EXPORT CFMutableBagRef   CFBagCreateMutableCopy(CFAllocatorRef allocator, CFIndex capacity, CFBagRef bag);

The CFBag function CFBagCreateMutableCopy has both “Create” and “Copy” in its name. It is a creation function because the function name contains the word “Create”. Note also that the first argument is of type CFAllocatorRef—this serves as a further hint. The “Copy” in this function is a hint that the function takes a CFBagRef argument and produces a duplicate of the object. It also refers to what happens to the element objects of the source collection: they are copied to the newly created bag. The secondary “Copy” and “NoCopy” substrings of function names indicate how objects owned by some source objects are treated—that is, whether they are copied or not.

上述案例介绍了createcopy的规则,与上面分析的一致,而且完全没有提到任何关于mutable相关的解释。下面再看一个API的定义

CFArrayCreateMutableCopy

theArray
The array to copy. The pointer values from the array are copied into the new array. However, the values are also retained by the new array.

  1. CFArrayCreateMutableCopy 方法的第三个参数,用来拷贝的原数组;
  2. 原数组的指针被拷贝到了新的数组;
  3. 指针所指向的对象被新的数组持有;

根据上面对深拷贝概念的对比,发现mutableCopy并没有完成深拷贝的过程,仅仅只是一个浅拷贝的过程。与copy不同的是,返回的容器对象是可变的,两者差异仅此而已。对容器对象,有以下特点:

  1. copymutableCopy都是浅拷贝,只拷贝容器对象本身,不拷贝元素对象,只拷贝元素引用;
  2. copy返回的容器是不可变的,mutableCopy返回的对象是可变的;

那么在Foundation框架中要完成深拷贝,可以使用框架提供的API:

initWithArray:copyItems:

总结

明确每一种概念所发生的变化,相关的问题都将迎刃而解。

  1. 指针拷贝
  2. 引用拷贝
  3. 对象拷贝
  4. 浅拷贝
  5. 深拷贝

Memory Management Programming Guide for Core Foundation
Advanced Memory Management Programming Guide
The Create Rule
CFArrayCreateMutableCopy
initWithArray:copyItems:

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

推荐阅读更多精彩内容