Swift中C語言指標的訪問和轉換方法
Swift 本身從設計上來說是一門非常安全的語言,在 Swift 的思想中,所有的引用或者變數的型別都是確定並且正確對應它們的實際型別的,你應當無法進行任意的型別轉換,也不能直接通過指標做出一些出格的事情。這種安全性在日常的程式開發中對於避免不必要的 bug,以及迅速而且穩定地找出程式碼錯誤是非常有幫助的。但是凡事都有兩面性,在高安全的同時,Swift 也相應地喪失了部分的靈活性。
現階段想要完全拋棄 C 的一套東西還是相當困難的,特別是在很多上古級別的 C API 框架還在使用 (或者被間接使用)。開發者,尤其是偏向較底層的框架的開發者不得不面臨著與 C API 打交道的時候,還是無法繞開指標的概念,而指標在 Swift 中其實並不被鼓勵,語言標準中也是完全沒有與指標完全等同的概念的。為了與龐大的 C 系帝國進行合作,Swift 定義了一套對 C 語言指標的訪問和轉換方法,那就是 UnsafePointer 和它的一系列變體。對於使用 C API 時如果遇到接受記憶體地址作為引數,或者返回是記憶體地址的情況,在 Swift 裡會將它們轉為 UnsafePointer<Type> 的型別,比如說如果某個 API 在 C 中是這樣的話:

其對應的 Swift 方法應該是:

我們這個 tip 所說的 UnsafePointer,就是 Swift 中專門針對指標的轉換。對於其他的 C 中基礎型別,在 Swift 中對應的型別都遵循統一的命名規則:在前面加上一個字母 C 並將原來的第一個字母大寫:比如 int,bool 和 char 的對應型別分別是CInt,CBool 和 CChar。在上面的 C 方法中,我們接受一個 int 的指標,轉換到 Swift 裡所對應的就是一個 CInt 的 UnsafePointer 型別。這裡原來的 C API 中已經指明瞭輸入的 num 指標的不可變的 (const),因此在 Swift 中我們與之對應的是 UnsafePointer 這個不可變版本。如果只是一個普通的可變指標的話,我們可以使用 UnsafeMutablePointer 來對應:

在 C 中,對某個指標進行取值使用的是 *,而在 Swift 中我們可以使用 memory 屬性來讀取相應記憶體中儲存的內容。通過傳入指標地址進行方法呼叫的時候就都比較相似了,都是在前面加上 & 符號,C 的版本和 Swift 的版本只在宣告變數的時候有所區別:

遵守這些原則,使用 UnsafePointer 在 Swift 中進行 C API 的呼叫應該就不會有很大問題了。
另外一個重要的課題是如何在指標的內容和實際的值之間進行轉換。比如我們如果由於某種原因需要涉及到直接使用 CFArray 的方法來獲取陣列中元素的時候,我們會用到這個方法:

因為 CFArray 中是可以存放任意物件的,所以這裡的返回是一個任意物件的指標,相當於 C 中的 void *。這顯然不是我們想要的東西。Swift 中為我們提供了一個強制轉換的方法 unsafeBitCast,通過下面的程式碼,我們可以看到應當如何使用類似這樣的 API,將一個指標強制按位轉成所需型別的物件:

unsafeBitCast 會將第一個引數的內容按照第二個引數的型別進行轉換,而不去關心實際是不是可行,這也正是 UnsafePointer 的不安全所在,因為我們不必遵守型別轉換的檢查,而擁有了在指標層面直接操作記憶體的機會。
其實說了這麼多,Apple 將直接的指標訪問冠以 Unsafe 的字首,就是提醒我們:這些東西不安全,親們能不用就別用了吧 (作為 Apple,另一個重要的考慮是如果避免指標的話可以減少很多系統漏洞)!在日常開發中,我們確實不太需要經常和這些東西打交道 (除了傳入 NSError 指標這個歷史遺留問題以外,而且在 Swift 2.0 中也已經使用異常機制替代了 NSError)。總之,儘可能地在高抽象層級編寫程式碼,會是高效和正確的有力保證。無數先輩已經用血淋淋的教訓告訴我們,要避免去做這樣的不安全的操作,除非你確實知道你在做的是什麼。
