天天看點

Swift—Core Foundation架構-備

Core Foundation架構是蘋果公司提供一套概念來源于Foundation架構,程式設計接口面向C語言風格的API。雖然在Swift中調用這種C語言風格的API比較麻煩,但是在OS X和iOS開發過程中,有時候使用CoreFoundation架構的API是非常友善的,例如在與C語言混合編碼的時候。

Core Foundation架構與Foundation架構緊密相關,他們具有與相同的接口,但是不同。Core Foundation架構是基于C語言風格的,而Foundation架構是基于Objective-C語言風格的。在OS X和iOS程式代碼中經常會有多種語言風格的代碼混合在一起的情況,這使得我們開發變得更加麻煩。 

資料類型映射

Core Foundation架構提供了一些不透明的資料類型,這些資料類型封裝了一些資料和操作,他們也可以稱為“類”,他們都繼承于CFType類,CFType是所用Core Foundation架構類型的根類。這些資料類型在Foundation架構中都有相應的資料類型與之對應,這些資料類型也有一些與Swift原生資料類型有對應關系。

Swift—Core Foundation架構-備

看看Swift原生類型與Core Foundation類型之間的轉換示例:

​​

import CoreFoundation  

import Foundation  

var cfstr1: CFString = "Hello,World"     //建立CFString字元串  

var str: String = cfstr1 as String           //将CFString字元串轉換為Swift原生字元串String  

var cfstr2: CFString = str                   //将Swift原生字元串String轉換為CFString字元串  

這個轉換過程中Core Foundation類型轉換為Swift原生類型是需要強制類型轉換的。

在Swift原生資料類型、Foundation架構資料類型和Core Foundation架構資料類型之間轉換過程中,雖然是大部分是可以零開銷橋接,零開銷并不意味着記憶體什麼都不用管。Swift類型記憶體管理是采用ARC,Foundation類型和Core Foundation類型記憶體管理都是采用MRC或ARC,CoreFoundation類型記憶體管理是基于C語言風格的,它有一個對象所有權的概念。

Objective-C的MRC記憶體管理

Core Foundation的記憶體管理與Objective-C的MRC記憶體管理密不可分,先介紹一下Objective-C的MRC記憶體管理。

所有Objective-C類都繼承NSObject類,每個NSObject對象都有一個内部計數器,這個計數器跟蹤對象的引用次數,被稱為“引用計數”(Reference Count,簡稱RC)。當對象被建立時候,引用計數為1。為了保證對象的存在,可以調用retain方法保持對象,retain方法會使其引用計數加1,如果不需要這個對象可以調用release或autorelease方法,release或autorelease方法使其引用計數減1。當對象的引用計數為0的時候,系統運作環境才會釋放對象記憶體。

引用計數示例如圖所示,首先在第①步調用者A中建立了一個NSObject對象,這時該對象引用計數為1。在第②步調用者B中想使用這個NSObject對象,于是使用NSObject對象引用,但是為了防止使用過程中NSObject對象被釋放,可以調用retain方法使引用計數加1,這時引用計數為2。在第③步調用者A中調用release或autorelease方法,使引用計數減1,這時引用計數為1。在第④步調用者C中調用release或autorelease方法,隻是獲得NSObject對象引用,并沒有調用retain、release或autorelease方法,是以沒有引起引用計數的變化。在第⑤步調用者B中調用release或autorelease方法使引用計數減1,這時引用計數為0。這個時候NSObject對象就記憶體就可以釋放了。

Swift—Core Foundation架構-備

來總結一下:

1. 誰建立或拷貝對象,他也一定要負責調用NSObject對象release或autorelease方法,使引用計數減1,如圖中調用者A在第①步,負責建立了NSObject對象,那麼調用者A也必須是負責使引用計數減1,見第④步。

2. 誰調用retain方法使引用計數加1,它也一定要負責調用NSObject對象release或autorelease方法,使引用計數減1,如圖中調用者B在第②步,調用者B調用NSObject對象retain方法使引用計數加1,那麼調用者B也必須是負責使引用計數減1,見第⑤步。

對象所有權

一個對象可以有一個或多個所有者,從所有者的角度看是對這個對象具有了“所有權”,從上圖中看,調用者A和調用者B是所有者,他們可能是一段程式,可能是一個對象。他們對NSObject對象具有所有權,不再使用時候他們應該負責放棄對象所有權,當對象沒有所有者時,引用計數為0,它才可以被釋放。

如上圖如果按照對象所有權解釋:調用者A建立或拷貝NSObject對象,這時調用者A就具有了NSObject對象的所有權,見第①步。調用者B調用NSObject對象retain方法,就獲得了也NSObject對象的所有權,見第②步。調用者A調用NSObject對象release方法,放棄NSObject對象的所有權,見第③步。調用者C隻是使用NSObject對象沒有獲得NSObject對象的所有權,見第④步。調用者B調用NSObject對象release方法,放棄NSObject對象的所有權,見第⑤步,但是調用者B使用這個NSObject對象過程中,由于其他調用者放棄所有權,導緻NSObject對象被釋放,那麼調用者B中程式就會發生運作期錯誤。

======================

記憶體托管對象

Swift中調用CoreFoundation函數獲得對象時候,對象分為:記憶體托管對象和記憶體非托管對象。

記憶體托管對象就是由編譯器幫助管理記憶體,我們不需要調用CFRetain函數獲得對象所有權,也不需要調用CFRelease函數放棄對象所有權。

獲得這些記憶體托管對象的方法,是采用了CF_RETURNS_RETAINED或CF_RETURNS_NOT_RETAINED注釋聲明,示例代碼:

-(CGPathRef)makeToPath CF_RETURNS_RETAINED  

{  

    UIBezierPath* triangle = [UIBezierPath bezierPath];  

    [triangle moveToPoint:CGPointZero];  

    [triangle addLineToPoint:CGPointMake(self.view.frame.size.width,0)];  

    [triangle addLineToPoint:CGPointMake(0, self.view.frame.size.height)];  

    [triangle closePath];  

    CGPathRef theCGPath = [triangle CGPath];  

    return CGPathCreateCopy(theCGPath);  

}  

記憶體托管對象使用起來比較簡單,不需要我們做額外的事情。

func CFStringCreateWithCString(_ alloc: CFAllocator!,   

        _ cStr: UnsafePointer<Int8>,  

         _ encoding: CFStringEncoding) -> CFString!  //記憶體托管對象  

func CFHostCreateCopy(_ alloc: CFAllocator?,  

         _ host: CFHost) -> Unmanaged<CFHost>    //記憶體非托管對象  

記憶體非托管對象

記憶體非托管對象就是記憶體需要程式員自己管理。這是由于在獲得對象的方法中沒有使用CF_RETURNS_RETAINED或CF_RETURNS_NOT_RETAINED注釋聲明,編譯器無法幫助管理記憶體。在具體使用時候我們可以上一節的方法判斷是否為非記憶體托管對象。

記憶體非托管對象使用起來有些麻煩,要根據獲得所有權方法,進行相應的處理。

1. 如果一個函數名中包含Create或Copy,則調用者獲得這個對象的同時也獲得對象所有權,傳回值Unmanaged<T>需要調用takeRetainedValue()方法獲得對象。調用者不再使用對象時候,Swift代碼中需要調用CFRelease函數放棄對象所有權,這是因為Swift是ARC記憶體管理的。

2. 如果一個函數名中包含Get,則調用者獲得這個對象的同時不會獲得對象所有權,傳回值Unmanaged<T>需要調用takeUnretainedValue()方法獲得對象。

示例代碼如下:

let host: CFHost = CFHostCreateWithName(kCFAllocatorDefault,   

        Ê"127.0.0.1").takeRetainedValue()  

let hostNames: CFArray = CFHostGetNames(host, nil)!.takeUnretainedValue()  

繼續閱讀