好吧,我從簡單的開始。看文章的主要內容。
大多數情況下,C語言指針可以通過兩種方式導入Swift:
UnsafePointer & ltT & gt
UnsafeMutablePointer & ltT & gt
t這裏是c類型的等價Swift類型,聲明為常量的指針作為UnsafePointer導入,非常數指針作為UnsafeMutablePoinger導入。
以下是壹些例子:
丙:
void my function(const int * myconstinpointer);
Swift:
func my function(myconstinpointer:unsafe pointer & lt;Int32 >)
丙:
void myOtherFunction(unsigned int * myunsignedint pointer);
Swift:
func myOtherFunction(myunsignedint pointer:unsafmutablepointer & lt;UInt32 & gt)
丙:
void itakeavoidepointer(void * avoidepointer);
Swift:
func itake avoid pointer(avoid pointer:unsafmutable pointer & lt;Void & gt)
如果您不知道指針的類型,例如聲明為前綴的指針,請使用COpaquePointer。
丙:
構造某物;
void iTakeAnOpaquePointer(struct SomeThing * SomeThing);
Swift:
func iTakeAnOpaquePointer(someThing:COpaquePointer)
傳遞指向Swift對象的指針
在許多情況下,傳遞指向Swift對象的指針就像使用inout運算符壹樣簡單,這與C語言中的and運算符類似。
Swift:
設myInt: = 42
我的函數(myInt)
var myUnsignedInt: UInt = 7
myOtherFunction(myUnsignedInt)
這裏有兩個非常重要但容易被忽略的細節。
使用inout運算符時,用var聲明的變量和用let聲明的常量分別轉換為UnsafePointer和UnsafeMutablePoinger。如果不註意原代碼中的類型,很容易出錯。妳可以試著把壹個UnsafeMutablePoinger傳遞到原來不安全的地方,編譯器會報錯。
2.該操作符只在Swift值和引用作為函數參數的上下文傳遞時生效,函數參數只接受兩種類型:UnsafePointer和UnsafeMutablePoinger。妳無法在其他上下文中獲得這些指針。例如,下面的代碼無效,將返回編譯錯誤。
Swift:
設x = 42
讓y = x
您可能需要不時地與API進行互操作。獲取或返回空指針而不是顯式類型。不幸的是,這種做法在C語言中非常普遍,以至於不可能指定壹個通用類型。
丙:
void takesAnObject(void * the object);
如果您確定函數需要獲取哪種參數,可以使用withUnsafePointer和unsafeBitCast將對象強制轉換為空指針。例如,假設takesAnObject需要獲取壹個指向int的指針。
var測試= 42
withUnsafePointer(test,{(ptr:UnsafePointer & lt;Int & gt)-& gt;使無效
var void ptr:unsafe pointer & lt;Void & gt= unsafeBitCast(ptr,UnsafePointer & ltVoid & gt。自我)
takesAnObject(voidPtr)
})
為了轉換它,我們首先需要調用withUnsafeMutablePointer,它包含兩個參數。
第壹個參數是t型inout運算符,第二個參數是(不安全指針)->;ResultType的閉包。函數通過指向第壹個參數的指針調用閉包,然後將其作為閉包的唯壹參數傳遞,最後函數返回閉包的結果。在上面的例子中,閉包的類型被設置為Void,所以不會返回值。返回值的示例如下:
let ret = withsunsafepointer(test,{(ptr:UnsafePointer & lt;Int & gt)-& gt;Int32英寸
var void ptr:unsafe pointer & lt;Void & gt= unsafeBitCast(ptr,UnsafePointer & ltVoid & gt。自我)
返回takesAnObjectAndReturnsAnInt(void ptr)
})
println(ret)
註意:需要自己修改指針,通過variant with unsafemutablepointer完成修改。
為方便起見,Swift還包括傳遞兩個指針的變體:
var x: Int = 7
var y: Double = 4
withUnsafePointers(x,y,{(ptr 1:unsafe pointer & lt;Int & gt,ptr 2:unsafe pointer & lt;Double & gt)-& gt;使無效
var void ptr 1:unsafe pointer & lt;Void & gt= unsafeBitCast(ptr1,UnsafePointer & ltVoid & gt。自我)
var void ptr 2:unsafe pointer & lt;Void & gt= unsafeBitCast(ptr2,UnsafePointer & ltVoid & gt。自我)
takesTwoPointers(void ptr 1,voidPtr2)
})
關於unsafeBitCast
UnsafeBitCast是壹個極其危險的操作。文件將其描述為“強行將某物轉換為與其他事物相同的長度”。我們之所以能在上面的代碼中安全地使用它,是因為我們只是簡單地轉換了不同類型的指針,而這些指針的長度是相同的。這就是為什麽我們必須先調用withUnsafePointer得到UnsafePointer,然後再轉換成UnsafePointer。
這壹開始可能會造成混亂,尤其是在處理和指針相同的類型時,比如Swift中的int(目前指針的長度是壹個字符,Int的長度也是壹個字符)。
例如,很容易犯以下錯誤:
var x: Int = 7
設xPtr = unsafeBitCast(x,UnsafePointer & ltVoid & gt。自我)
這段代碼的意圖是獲取壹個指針,並將其傳遞給x,這樣會造成誤解,雖然編譯可以傳遞並運行,但會導致意外錯誤。這是因為C API不是獲取壹個指針並將其傳遞給X,而是接收壹個位於0x7或其他位置的指針。
因為unsafeBitCast要求類型的長度相等,所以當試圖轉換Int8或字節整數時,它並不那麽陰險。
var x: Int8 = 7
設xPtr = unsafeBitCast(x,UnsafePointer & ltVoid & gt。自我)
這段代碼只會導致unsafeBitCast拋出異常並使程序崩潰。
在C語言中與結構交互
讓我們用實際例子來展示這壹部分。如果您想要檢索計算機正在運行的系統信息,有壹個CAPI: UNAME (2)可以達到目的。它接收指向數據結構的指針,並用系統信息填充所提供的對象,例如OS名稱和版本或硬件標識符。但這裏有壹個問題。導入Swift的結構如下:
結構名稱{
var sysname: (Int8,Int8,253次,Int8)
變量節點名:(Int8,Int8,253次,Int8)
var發布:(Int8,Int8,253次,Int8)
var版本:(Int8,Int8,253倍,Int8)
var機:(Int8,Int8,253次,Int8)
}
Swift將C中的數組文字作為元組導入,默認的初始化程序要求每個字段都有壹個值,所以如果按照Swift通常的方式做,就會變成:
var name = utsname(sysname: (0,0,0,,0),nodename: (0,0,0,,0),等等)
utsname(名稱)
var machine =名稱.機器
打印機
這不是壹個好方法。還有壹個問題。因為utsname中的machine字段是壹個tuple,所以在使用println時,會輸出256位的Int8,但實際上只有字符串的前幾個ASCII值才是我們需要的。
那麽,如何解決這個問題呢?
Swift中的UnsafeMutablePointer提供了alloc(Int)和dealloc(Int)兩種方法,分別用來分配和解除分配模板T的量化參數。我們可以使用這些API來簡化我們的代碼:
let name = unsafmutablepointer & lt;utsname & gt。分配(1)
uname(姓名)
let machine = with unsafepointer(name . memory . machine,{(ptr)-& gt;字符串?在
let int8Ptr = unsafeBitCast(ptr,UnsafePointer & ltInt8 >。自我)
返回String.fromCString(int8Ptr)
})
name.dealloc(1)
如果讓m =機器{
打印長度(米)
}
第壹步是調用withUnsafePointer,把機器的元組傳遞給它,通知它我們的閉包會返回壹個額外的字符串。
在閉包中,我們將指針轉換為UnsafePointer,這是值的最等價表達式。此外,Swift的字符串包含壹個初始化UnsafePointer的類方法,其中CChar是Int8類型的別名,因此我們可以將我們的新指針傳遞給初始化器,並返回所需的信息。
在獲得withUnsafePointer的結果後,我們可以測試它是否是let的條件語句,並打印出結果。對於本例,它輸出預期的字段“x86_64”。
摘要
最後,免責聲明。在Swift中使用不安全的API應被視為最後手段,因為它們具有潛在的不安全性。當我們將遺留的C和Objective-C代碼轉換成Swift時,很有可能我們將繼續需要這些API來與現有工具兼容。但是,當使用無不安全指針和unsafeBitCast作為首選時,我們應該始終持懷疑態度,尋找其他更好的解決方案。
新代碼要盡可能符合語言習慣,不要在Swift代碼中使用不安全的API。作為壹名開發人員,妳應該知道如何使用妳的工具,以及在哪裏使用它們,在哪裏不使用它們。Swift為OS X和iOS開發帶來了現代化,我們必須尊重它的理念。