當前位置:成語大全網 - 書法字典 - 如何在Swift中使用UnsafeMutablePointer

如何在Swift中使用UnsafeMutablePointer

如果妳構建壹個swift項目,當妳創建壹個C文件的時候,然後添加壹些方法,比如返回壹個char*(已經橋接)類型的方法,然後當妳在Swift中編寫這個C定義的方法的時候,xcode會自動轉換成Swift中的類型。Char*對應UnsafeMutablePointer,const char*是UnsafePointer。

好吧,我從簡單的開始。看文章的主要內容。

大多數情況下,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開發帶來了現代化,我們必須尊重它的理念。