讀古今文學網 > iOS編程基礎:Swift、Xcode和Cocoa入門指南 > 12.10 CFTypeRefs的內存管理 >

12.10 CFTypeRefs的內存管理

CFTypeRef純粹是C中與Objective-C對象的等價之物。它是指向某個實現「細節未知」的C結構體的指針(參見附錄A),「細節未知」指的是該結構體沒有可直接訪問的組件。這個結構體作為一個假對像;CFTypeRef等價於對像類型。不過,它並不是對像類型,處理CFTypeRef的代碼也不是面向對象的;CFTypeRef沒有屬性和方法,你也不能向其發送任何消息。可以完全通過全局函數來使用CFTypeRefs,它們實際上是C函數。

在Objective-C中,CFTypeRef類型的特性是名字以Ref後綴結尾。不過在Swift中已經去掉了這個Ref後綴。

下面是用於繪製漸變色的Swift代碼:


let con = UIGraphicsGetCurrentContext!
let locs : [CGFloat] = [ 0.0, 0.5, 1.0 ]
let colors : [CGFloat] = [
    0.8, 0.4, // starting color, transparent light gray
    0.1, 0.5, // intermediate color, darker less transparent gray
    0.8, 0.4, // ending color, transparent light gray
]
let sp = CGColorSpaceCreateDeviceGray
let grad =
    CGGradientCreateWithColorComponents (sp, colors, locs, 3)
CGContextDrawLinearGradient (
    con, grad, CGPointMake(89,0), CGPointMake(111,0), )
  

在上述代碼中,con是個CGContextRef,在Swift中則是CGContext;sp是個CGColorSpaceRef,在Swift中則是CGColorSpace;grad是個CGGradientRef,在Swift中則是CGGradient。它們都是CFTypeRefs。其代碼並不是面向對象的;而是對全局C函數的一系列調用。

不過,CFTypeRef假對象也是個對象。這意味著被指針指向的C結構體實際上與引用所指向的類實例是一樣的。這也意味著我們需要管理CFTypeRefs的內存。特別地,CFTypeRef假對象也有一個保持計數!其使用方式與真實對像別無二致,也遵循著內存管理的黃金法則。如果其所有者希望它一直存在,那麼CFTypeRef就必須要保持;當所有者不再需要它時,我們得將其釋放。

在Objective-C中,對於CFTypeRefs使用的黃金法則就是,如果通過一個函數(其名字包含了單詞Create或Copy)獲得了一個CFTypeRef對象,那麼其保持計數就會增加。此外,如果擔心對象的存活時間,那就需要顯式調用CFRetain函數增加其保持計數來保持它。要想平衡Create、Copy與CFRetain調用,最終需要釋放對象。在默認情況下,這是通過調用CFRelease函數實現的;不過,一些CFTypeRefs擁有自己專門的對象釋放函數。比如,CGPath就有一個專門的CGPathRelease函數。

不過在Swift中,你永遠不需要調用CFRetain或任何形式的CFRelease;事實上,你也無法調用。Swift會在背後自動調用。

可以將CFTypeRefs看作存在於兩個世界中:純C的CFTypeRef世界,以及面向對像內存管理的Swift世界。在獲取到一個CFTypeRef假對像時,它會從CFTypeRef世界來到Swift世界。從那時起直到使用完,你都需要管理其內存。Swift知道這一點,大多數情況下,Swift本身都會使用黃金法則並進行正確的內存管理。這樣,上面展示的繪製漸變色的代碼實際上就會正確地管理好內存。在Objective-C中,我們需要釋放sp與grad,因為它們是通過Create調用創建的;不這麼做就會導致內存洩漏。不過在Swift中就沒這個必要了,因為它會幫我們做這些事情。

在Swift中使用CFTypeRefs要比在Objective-C中簡單。在Swift中,你可以將CFTypeRef假對像看作真實對象!比如,你可以在Swift中將CFTypeRef賦給屬性,或將其作為參數傳遞給Swift函數,其內存會得到正確的管理;在Objective-C中,這些事情都很棘手。

不過,你可能會通過一些缺乏內存管理信息的API來接收CFTypeRef。這樣的值應該引起你的注意,因為它會以包裝了實際CFTypeRef的非托管值的形式進入Swift中。這會產生一個警告,因為Swift並不知道如何對這個假對像進行內存管理。實際上,只有通過調用Unmanaged對象的takeRetainedValue或takeUnretainedValue方法展開CFTypeRef後才能繼續。你需要通過這兩個方法來告訴Swift該如何正確管理這個對象的內存。對於通過名字中帶有Create或Copy的內建函數所返回的CFTypeRef來說,請調用takeRetainedValue;否則請調用takeUnretainedValue。