最近學習Swift內存相關的東西,接觸到了Unmanaged這個類型,在網上看了很多文檔,個人感覺講的比較晦澀,所以寫下這篇筆記方便自己理解和總結。
Unmanaged解決的是在Swift中調用C函數(shù)時,C函數(shù)返回CoreFoundation類型的對象內存管理相關的問題,它的出現(xiàn)屬于歷史原因而作為臨時的過渡方案。
在ARC下,所有的OC對象和從OC方法返回的CoreFoundation類型的對象都能由編譯器自動管理內存。而通過C語言返回的CoreFoundation類型的對象仍然需要CFRetain、CFRelease來手動管理引用計數(shù),或者橋接到OC對象上。
蘋果通過在C函數(shù)命名中使用Create\Copy、Get字眼,讓調用者明白返回的對象是否被調用者持有。比如我們調用包含Create\Copy的C函數(shù)返回的對象,需要我們對其使用CFRelease函數(shù)進行手動釋放。
CFAttributedStringRef attrStrRef = CFAttributedStringCreate(kCFAllocatorDefault, (CFStringRef)@"Hello", NULL);
CFRelease(attrStrRef);//需要手動釋放
而調用包含Get的C函數(shù)返回的對象則不需要手動釋放,如果我們需要持有這個返回對象,則需要對其調用CFRetain函數(shù)。
Swift只支持ARC,所以沒有retain、release這些方法,在Swift和OC混編的時候,編譯器也能很好的幫我們自動管理內存,除了上面說的C函數(shù)返回CoreFundation對象。
舉個例子,我們使用蘋果的命名規(guī)范聲明一個C函數(shù):
//聲明
CFStringRef CreateJoinedString(CFStringRef s1, CFStringRef s2);
//實現(xiàn)
CFStringRef CreateJoinedString(CFStringRef s1, CFStringRef s2) {
CFMutableStringRef resultString = CFStringCreateMutableCopy(NULL, 0, s1);
CFStringAppend(resultString, s2);
return resultString;
}
然后在Swift中進行調用:

可以看到橋接到Swift中的方法的返回值是一個Unmanaged<CFString>!類型,為什么會返回這個類型?因為在OC中編譯器都不能幫我們自動管理C函數(shù)返回的CoreFoundation對象,更別說Swift橋接調用C函數(shù)了。所以編譯器這時候不知道如何管理這個C函數(shù)返回對象的引用計數(shù),在翻譯成Swift代碼時將其包裝成了Unmanaged類型的對象。這個類型現(xiàn)在只關注如下兩個方法。
//都是取值,區(qū)別在于
//這個方法在取值時不會改變引用值的引用計數(shù)
public func takeUnretainedValue() -> Instance
//這個方法返回引用值并對引用值進行一次release(引用計數(shù)減1)
public func takeRetainedValue() -> Instance
這其實就是將對象的內存管理交給調用者自己處理,相當于在Swift中變相提供了手動release操作。通過調用需要我們手動釋放內存的函數(shù)而獲得的對象時,使用takeRetainedValue進行取值,否則使用takeUnretainedValue,也就是蘋果在CoreFoundation中使用Create/Get命名方式的那些函數(shù)。
當然我們自己在寫這種C函數(shù)時要避免在Swift中被包裝成Unmanaged類型,可以使用CF_IMPLICIT_BRIDGING_ENABLED和CF_IMPLICIT_BRIDGING_DISABLED這兩個宏將函數(shù)聲明包裹起來,這是告訴clang編譯器:不需要為Swift橋接審查和處理這些函數(shù)的CoreFoundation類型(即Annotated APIs,向編譯器注明該方法可自動管理)
CF_IMPLICIT_BRIDGING_ENABLED
CFStringRef CreateJoinedString(CFStringRef s1, CFStringRef s2);
CF_IMPLICIT_BRIDGING_DISABLED
再回到Swift調用可以看到返回值不在是Unmanaged類型,而是C函數(shù)一樣直接返回CFString

現(xiàn)在大部分CoreFoundation的方法都已經面向Swift進行了優(yōu)化,返回值沒有被Unmanaged包裝,但還有部分API沒有被注明可自動管理,比如AddressBook。
查看AddressBook的方法聲明,可以看到很多方法的返回值類型是Unmanaged類型

可能由于蘋果來不及對這些API進行處理吧,所以正如文初所說,Umagnage是因為歷史原因(Swift 的API橋接OC的API,OC底層又是C的實現(xiàn))和作為過渡方案的產物。隨著Swift的完善,可能會越來越少甚至不會再見到這樣的API。自己寫的C函數(shù)要盡量避免這樣的情況。