今天来谈下深浅拷贝【copy 和 mutableCopy】
想写这篇文章,是因为看到了一个有意思的文章,对于其中的一些观点不太认同,所以想简单分析一下深浅拷贝
先看一段代码,稍后会对代码进行分析
1 | -(void)testOne |
分析:
很简单的创建一个数组,对其进行copy和mutableCopy操作,打印其内存地址。结果看图会知道:
test和testCopy 内存地址是一样的,而testMutableCopy的内存地址是和test不同的。
小总结:
copy
只是拷贝指向对象的指针,并没有出现新的内存地址,我们称之为浅拷贝:mutableCopy
拷贝整个对象内存到另一块内存中,是产生了新的内存地址。
再看下面的一段代码:
1 | -(void)testTwo |
分析:
上面这段代码会和之前的不一样,我们这里创建的是一个可变数组,然后对可变数组进行copy
和mutableCopy
操作。发现打印其内存地址都不一样,
这说明对可变数组进行copy
和mutableCopy
都是深拷贝。
但你会发现copy
得到的数组不能添加删除操作,这是因为copy
得到的是不可变数组。
小总结:
可变数组的copy
和mutableCopy
都是深拷贝
最后一段代码:
1 | -(void)testThree |
分析:
通过mutableCopy
得到的数组,你会发现通过mutableCopy
得到的数组中的元素地址是不变的。如果你往数组的元素中添加或删除元素,会发现newTest和testMutableCopy4中的元素都发生了改变。
小总结:
集合对象的内容复制(mutableCopy)仅限于对象本身,对象中的元素仍然是指针复制。
关于这点其实苹果官网文档中CopyFunctions
给了解释,看下以下介绍:
关于复制的介绍:
Copy Functions
In general, a standard copy operation, which might also be called simple assignment, occurs when you use the = operator to assign the value of one variable to another. The expression myInt2 = myInt1, for example, causes the integer contents of myInt1 to be copied from the memory used by myInt1 into the memory used by myInt2. Following the copy operation, two separate areas of memory contain the same value. However, if you attempt to copy a Core Foundation object in this way, be aware that you will not duplicate the object itself, only the reference to the object.
For example, someone new to Core Foundation might think that to make a copy of a CFString object she would use the expression myCFString2 = myCFString1. Again, this expression does not actually copy the string data. Because both myCFString1 and myCFString2 must have the CFStringRef type, this expression only copies the reference to the object. Following the copy operation, you have two copies of the reference to the CFString. This type of copy is very fast because only the reference is duplicated, but it is important to remember that copying a mutable object in this way is dangerous. As with programs that use global variables, if one part of your application changes an object using a copy of the reference, there is no way for other parts of the program which have copies of that reference to know that the data has changed.
If you want to duplicate an object, you must use one of the functions provided by Core Foundation specifically for this purpose. Continuing with the CFString example, you would use CFStringCreateCopy to create an entirely new CFString object containing the same data as the original. Core Foundation types which have “CreateCopy” functions also provide the variant “CreateMutableCopy” which returns a copy of an object that can be modified.
浅拷贝:
Shallow Copy
Copying compound objects, objects such as collection objects that can contain other objects, must also be done with care. As you would expect, using the = operator to perform a copy on these objects results in a duplication of the object reference. In contrast to simple objects like CFString and CFData, the “CreateCopy” functions provided for compound objects such as CFArray and CFSet actually perform a shallow copy. In the case of these objects, a shallow copy means that a new collection object is created, but the contents of the original collection are not duplicated—only the object references are copied to the new container. This type of copy is useful if, for example, you have an array that’s immutable and you want to reorder it. In this case, you don’t want to duplicate all of the contained objects because there’s no need to change them—and why use up that extra memory? You just want the set of included objects to be changed. The same risks apply here as with copying object references with simple types.
深拷贝:
Deep Copy
When you want to create an entirely new compound object, you must perform a deep copy. A deep copy duplicates the compound object as well as the contents of all of its contained objects. The current release of Core Foundation includes a function that performs deep copying of a property list (see CFPropertyListCreateDeepCopy). If you want to create deep copies of other structures, you could perform the deep copy yourself by recursively descending into the compound object and copying all of its contents one by one. Take care in implementing this functionality as compound objects can be recursive—they may directly or indirectly contain a reference to themselves—which can cause a recursive loop.
小总结:
我们通过对集合类对象进行mutableCopy得到的新对象,从某种意义上来说,并不是真正的深层次的复制,严格意义上说:它只是一个单层次的深复制。原因上面已说明:集合对象的内容复制(mutableCopy)仅限于对象本身,对象中的元素仍然是指针复制。
那如果需要深层次的复制,就需要以递归的形式找到集合中的元素对象,再对元素对象进行mutableCopy,以此来实现深层次的复制。但Apple并不提倡这种方式,因为这可能会导致递归循环。
最后总结:
- 不管是集合类对象还是非集合类对象,copy和mutableCopy时,都遵循以下准则:
- copy得到的都是不可变对象(imutable),所以对其copy返回的对象做可变对象的操作,都会崩溃。
- mutableCopy返回的是可变对象(mutable)
- 在非集合类对象中:
- 对不可变对象进行copy操作,是指针复制,其内存地址不变。
- 对不可变对象进行mutableCopy操作,是内容复制,其内存地址改变。
- 对可变对象进行copy和mutableCopy操作,都是内容复制,其内存地址改变。
- 在集合类对象中:
- 对不可变对象进行copy操作,是指针复制,其内存地址不变。
- 对不可变对象进行mutableCopy操作,是内容复制,其内存地址改变。
- 对可变对象进行copy和mutableCopy操作,都是内容复制,其内存地址改变。但集合对象的内容复制仅限于对象本身,对象中的元素仍然是指针复制。
最后可以看下这个Apple的官方文档《苹果官网文档-Copying》