淺談Unmanaged

最近學(xué)習(xí)Swift內(nèi)存相關(guān)的東西,接觸到了Unmanaged這個類型,在網(wǎng)上看了很多文檔,個人感覺講的比較晦澀,所以寫下這篇筆記方便自己理解和總結(jié)。

Unmanaged解決的是在Swift中調(diào)用C函數(shù)時,C函數(shù)返回CoreFoundation類型的對象內(nèi)存管理相關(guān)的問題,它的出現(xiàn)屬于歷史原因而作為臨時的過渡方案。

在ARC下,所有的OC對象和從OC方法返回的CoreFoundation類型的對象都能由編譯器自動管理內(nèi)存。而通過C語言返回的CoreFoundation類型的對象仍然需要CFRetain、CFRelease來手動管理引用計數(shù),或者橋接到OC對象上。

蘋果通過在C函數(shù)命名中使用Create\Copy、Get字眼,讓調(diào)用者明白返回的對象是否被調(diào)用者持有。比如我們調(diào)用包含Create\Copy的C函數(shù)返回的對象,需要我們對其使用CFRelease函數(shù)進(jìn)行手動釋放。

CFAttributedStringRef attrStrRef = CFAttributedStringCreate(kCFAllocatorDefault, (CFStringRef)@"Hello", NULL);
CFRelease(attrStrRef);//需要手動釋放

而調(diào)用包含Get的C函數(shù)返回的對象則不需要手動釋放,如果我們需要持有這個返回對象,則需要對其調(diào)用CFRetain函數(shù)。

Swift只支持ARC,所以沒有retain、release這些方法,在Swift和OC混編的時候,編譯器也能很好的幫我們自動管理內(nèi)存,除了上面說的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中進(jìn)行調(diào)用:


圖1

可以看到橋接到Swift中的方法的返回值是一個Unmanaged<CFString>!類型,為什么會返回這個類型?因為在OC中編譯器都不能幫我們自動管理C函數(shù)返回的CoreFoundation對象,更別說Swift橋接調(diào)用C函數(shù)了。所以編譯器這時候不知道如何管理這個C函數(shù)返回對象的引用計數(shù),在翻譯成Swift代碼時將其包裝成了Unmanaged類型的對象。這個類型現(xiàn)在只關(guān)注如下兩個方法。

    //都是取值,區(qū)別在于
    //這個方法在取值時不會改變引用值的引用計數(shù)
    public func takeUnretainedValue() -> Instance
    //這個方法返回引用值并對引用值進(jìn)行一次release(引用計數(shù)減1)
    public func takeRetainedValue() -> Instance

這其實就是將對象的內(nèi)存管理交給調(diào)用者自己處理,相當(dāng)于在Swift中變相提供了手動release操作。通過調(diào)用需要我們手動釋放內(nèi)存的函數(shù)而獲得的對象時,使用takeRetainedValue進(jìn)行取值,否則使用takeUnretainedValue,也就是蘋果在CoreFoundation中使用Create/Get命名方式的那些函數(shù)。

當(dāng)然我們自己在寫這種C函數(shù)時要避免在Swift中被包裝成Unmanaged類型,可以使用CF_IMPLICIT_BRIDGING_ENABLEDCF_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調(diào)用可以看到返回值不在是Unmanaged類型,而是C函數(shù)一樣直接返回CFString


圖2

現(xiàn)在大部分CoreFoundation的方法都已經(jīng)面向Swift進(jìn)行了優(yōu)化,返回值沒有被Unmanaged包裝,但還有部分API沒有被注明可自動管理,比如AddressBook。

查看AddressBook的方法聲明,可以看到很多方法的返回值類型是Unmanaged類型


圖3

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

參考:https://nshipster.cn/unmanaged/

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容