“為學(xué)日益,為道日損,損之又損,以至于無為,無為而無不為”。用自己的思考去理解問題,從問題的源頭探索,才有可能尋找到最接近設(shè)計(jì)者思想的答案。網(wǎng)上有非常多的博客,博客內(nèi)容良莠不齊,且絕大多數(shù)是為被大眾證實(shí),僅僅是一面之詞。日常閱讀,日常解惑,有這兩篇文檔可以閱讀,分別是更新在MRR、ARC時(shí)代的兩篇文章。
Updated: 2009-10-21
Memory Management Programming Guide for Core Foundation
Updated: 2012-07-17
Advanced Memory Management Programming Guide
下面只是筆者對(duì)上面兩篇文章一點(diǎn)讀后感。
引用拷貝(Reference Copy)
引用拷貝
這個(gè)名詞在文章中并沒有直接指出,但有相關(guān)的概念提示,例如截圖第二段中,第三行:copies the reference to the object
,第五行中提到:the reference is duplicated
,拷貝的結(jié)果是一個(gè)新的指針,故亦稱之為指針拷貝
。
基本數(shù)據(jù)類型
下面以整數(shù)的拷貝為例:
myInt2 = myInt1
-
myInt1
和myInt2
分別是兩個(gè)獨(dú)立的內(nèi)存空間; -
myInt1
的值拷貝進(jìn)myInt2
Core Foundation類型
myCFString2 = myCFString1
-
myCFString2
和myCFString1
都是CFStringRef類型,僅僅只是拷貝對(duì)象的引用,并不能拷貝對(duì)象的內(nèi)容; - 拷貝一個(gè)對(duì)象的類型(引用)是非常快的,因?yàn)闆]有拷貝對(duì)象的內(nèi)容;
- 拷貝一個(gè)可變對(duì)象的的引用是非常危險(xiǎn)的操作。因?yàn)樵谀硞€(gè)地方修改了這個(gè)對(duì)象的數(shù)據(jù),其他地方的引用是無法知道數(shù)據(jù)已經(jīng)被修改了;(數(shù)據(jù)的修改可以是對(duì)象本身被修改,也可以是對(duì)象的某個(gè)屬性被修改)
-
CFStringCreateCopy
拷貝一個(gè)全新的對(duì)象,對(duì)象內(nèi)容與原始數(shù)據(jù)一模一樣,當(dāng)我們?cè)谛薷倪@個(gè)新對(duì)象時(shí),是不會(huì)對(duì)舊對(duì)象造成任何影響;
淺拷貝(Shallow Copy)
上面提到了 CFStringCreateCopy
,這個(gè)方法僅僅是適用于字符串的copy,而對(duì)于array,同樣有CFArrayCreateCopy
方法。CFxxtypexxCreateCopy
系列的方法都會(huì)拷貝一個(gè)新的對(duì)象。
單一對(duì)象
例如CFStringRef、CFDataRef,在使用CFxxtypexxCreateCopy
時(shí),都會(huì)拷貝一個(gè)全新的對(duì)象。
容器對(duì)象
- 對(duì)容器對(duì)象本身,拷貝一個(gè)全新的容器對(duì)象;
- 新容器中的元素,只是拷貝的元素的引用;
- 文章給出的解釋是,你只希望有一個(gè)不可變數(shù)組去記錄這些元素,也就是你本身就不想去改變他們,那就沒有必要分配額外的空間。
深拷貝(Deep Copy)
- 深拷貝可以拷貝出一個(gè)全新的容器對(duì)象,對(duì)象本身以及容器對(duì)象里面的元素都會(huì)被拷貝;
-
CFPropertyListCreateDeepCopy
,對(duì)于給定的list,遞歸調(diào)用CFxxtypexxCreateCopy
方法,拷貝容器中的元素; - 如果需要深拷貝其他數(shù)據(jù)結(jié)構(gòu),需要自己手動(dòng)遞歸拷貝容器對(duì)象里面的所有元素;
- 文章也指出,在遞歸的時(shí)候需要避免
遞歸循環(huán)
;
從上面的一些特點(diǎn)不難分析出,深拷貝是一種概念
,而不是某個(gè)具體的操作。不管是集合對(duì)象,還是自定義的實(shí)例對(duì)象,除了對(duì)象本身,他們還依賴一些其他對(duì)象,我們可以通過引用的方式來表示其他對(duì)象。當(dāng)對(duì)象本身被拷貝時(shí),僅僅只會(huì)開辟這個(gè)對(duì)象需要的空間,然后將原對(duì)象中的數(shù)據(jù)一比一復(fù)制一份存到這塊內(nèi)存,到這里就是淺拷貝的過程。如果是深拷貝,還需要把所有引用指向的對(duì)象再拷貝一份,讓引用也指向新的對(duì)象,這樣才能將這個(gè)對(duì)象以及這個(gè)對(duì)象所依賴的其他對(duì)象完全與原來的那份隔離開。
滿足以下兩個(gè)過程的拷貝可以稱作深拷貝:
- 拷貝對(duì)象本身;
- 遞歸拷貝對(duì)象所引用的對(duì)象;
copy 和 mutableCopy
上面僅僅是對(duì)平時(shí)一直遇到,但沒有理解透徹的概念做了一個(gè)整理,加深對(duì)這些概念的理解。上面的三個(gè)拷貝都是在MRR模式下,Core Foundation
框架中的概念,而且有API支持。在ARC模式下,我們遇到最多的其實(shí)是strong/copy/mutableCopy
這些關(guān)鍵詞。
-
retain/strong,引用拷貝
,對(duì)象的引用計(jì)數(shù)+1。圖中classB對(duì)對(duì)象retain,引用計(jì)數(shù)變?yōu)?; -
copy,對(duì)象拷貝
,會(huì)拷貝一個(gè)新的對(duì)象。圖中classC對(duì)原對(duì)象copy,拷貝一個(gè)新的對(duì)象,新對(duì)象引用計(jì)數(shù)為1,圖中虛線流程;
這張圖可以佐證上面的一些概念。Foundation
框架中的copy
只表示一件事——拷貝對(duì)象
,所以copy
與淺拷貝、深拷貝
的概念無直接聯(lián)系。在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 typeCFAllocatorRef
—this serves as a further hint. The “Copy” in this function is a hint that the function takes aCFBagRef
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.
上述案例介紹了create
和copy
的規(guī)則,與上面分析的一致,而且完全沒有提到任何關(guān)于mutable
相關(guān)的解釋。下面再看一個(gè)API的定義
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.
- CFArrayCreateMutableCopy 方法的第三個(gè)參數(shù),用來拷貝的原數(shù)組;
- 原數(shù)組的指針被拷貝到了新的數(shù)組;
- 指針?biāo)赶虻膶?duì)象被新的數(shù)組持有;
根據(jù)上面對(duì)深拷貝概念
的對(duì)比,發(fā)現(xiàn)mutableCopy
并沒有完成深拷貝的過程,僅僅只是一個(gè)淺拷貝的過程。與copy
不同的是,返回的容器對(duì)象是可變的,兩者差異僅此而已。對(duì)容器對(duì)象,有以下特點(diǎn):
-
copy
和mutableCopy
都是淺拷貝,只拷貝容器對(duì)象本身,不拷貝元素對(duì)象,只拷貝元素引用; -
copy
返回的容器是不可變的,mutableCopy
返回的對(duì)象是可變的;
那么在Foundation
框架中要完成深拷貝,可以使用框架提供的API:
總結(jié)
明確每一種概念所發(fā)生的變化,相關(guān)的問題都將迎刃而解。
- 指針拷貝
- 引用拷貝
- 對(duì)象拷貝
- 淺拷貝
- 深拷貝
Memory Management Programming Guide for Core Foundation
Advanced Memory Management Programming Guide
The Create Rule
CFArrayCreateMutableCopy
initWithArray:copyItems: