程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> 更多關於編程 >> Swift中的指針操作和應用具體引見

Swift中的指針操作和應用具體引見

編輯:更多關於編程

Swift中的指針操作和應用具體引見。本站提示廣大學習愛好者:(Swift中的指針操作和應用具體引見)文章只能為提供參考,不一定能成為您想要的結果。以下是Swift中的指針操作和應用具體引見正文


Apple希冀在Swift中指針可以或許盡可能削減退場概率,是以在Swift中指針被映照為了一個泛型類型,而且還比擬籠統。這在必定水平上形成了在Swift中指針應用的艱苦,特殊是對那些其實不熟習指針,也沒有若干指針操作經歷的開辟者(包含我本身也是)來講,在Swift中應用指針確切是一個挑釁。在這篇文章裡,我願望能從最根本的應用開端,總結一下在Swift中應用指針的一些罕見方法和場景。這篇文章假定你至多曉得指針是甚麼,假如對指針自己的概念不太清晰的話,可以先看看這篇五分鐘C指針教程(或許它的中文版本),應當會很有贊助。

初步

在Swift中,指針都應用一個特別的類型來表現,那就是UnsafePointer<T>。遵守了Cocoa的一向弗成變准繩,UnsafePointer<T> 也是弗成變的。固然對應地,它還有一個可變變體,UnsafeMutablePointer<T>。絕年夜部門時光裡,C中的指針都邑被以這兩品種型引入到Swift中:C中const潤飾的指針對應UnsafePointer(最多見的應當就是C字符串的const char *了),而其他可變的指針則對應UnsafeMutablePointer。除此以外,Swift中存在表現一組持續數據指針的UnsafeBufferPointer<T>,表現非完全構造的不通明指針COpaquePointer等等。別的你能夠曾經留意到了,可以或許肯定指向內容的指針類型都是泛型的struct,我們可以經由過程這個泛型來對指針指向的類型停止束縛以供給必定平安性。

關於一個UnsafePointer<T>類型,我們可以經由過程memory屬性對其停止取值,假如這個指針是可變的UnsafeMutablePointer<T> 類型,我們還可以經由過程memory對它停止賦值。好比我們想要寫一個應用指針直接操作內存的計數器的話,可以這麼做:

func incrementor(ptr: UnsafeMutablePointer<Int>) { 
    ptr.memory += 1 

var a = 10 
incrementor(&a) 
a  // 11 

這裡和C的指針應用相似,我們經由過程在變量名後面加上&符號便可以將指向這個變量的指針傳遞到接收指針作為參數的辦法中去。在下面的incrementor中我們經由過程直接操作memory屬性轉變了指針指向的內容。

與這類做法相似的是應用Swift的inout症結字。我們在將變量傳入inout參數的函數時,異樣也應用&符號表現地址。不外差別是在函數體外部我們不須要處置指針類型,而是可以對參數直接停止操作。


func incrementor1(inout num: Int) { 
    num += 1 

var b = 10 
incrementor1(&b) 
b  // 11 

固然&在參數傳遞時表現的意義和C中一樣,是某個“變量的地址”,然則在Swift中我們沒有方法直接經由過程這個符號獲得一個UnsafePointer的實例。須要留意這一點和C有所分歧:


// 沒法編譯 
let a = 100 
let b = &a 

指針初始化和內存治理

在Swift中不克不及直接取到現有對象的地址,我們照樣可以創立新的UnsafeMutablePointer對象。與Swift 中其他對象的主動內存治理分歧,關於指針的治理,是須要我們手動停止內存的請求和釋放的。一個 UnsafeMutablePointer的內存有三種能夠狀況:

1.內存沒有被分派,這意味著這是一個 null 指針,或許是之前曾經釋放過;
2.內存停止了分派,然則值還沒有被初始化;
3.內存停止了分派,而且值曾經被初始化。

個中只要第三種狀況下的指針是可以包管正常應用的。UnsafeMutablePointer的初始化辦法(init)完成的都是從其他類型轉換到UnsafeMutablePointer的任務。我們假如想要新建一個指針,須要做的是應用alloc:這個類辦法。該辦法接收一個num: Int作為參數,將向體系請求num個數的對應泛型類型的內存。上面的代碼請求了一個Int年夜小的內存,並前往指向這塊內存的指針:

var intPtr = UnsafeMutablePointer<Int>.alloc(1) 
// "UnsafeMutablePointer(0x7FD3A8E00060)" 

接上去應當做的是對這個指針的內容停止初始化,我們可使用initialize:辦法來完成初始化:

intPtr.initialize(10) 
// intPtr.memory 為 10 

在完成初始化後,我們便可以經由過程memory來操作指針指向的內存值了。

在應用以後,我們最好盡快釋放指針指向的內容和指針自己。與initialize:配對應用的destroy用來燒毀指針指向的對象,而與alloc:對應的dealloc:用來釋放之前請求的內存。它們都應當被配對應用:

intPtr.destroy() 
intPtr.dealloc(1) 
intPtr = nil 

留意:其其實這裡關於Int如許的在C中映照為int的“平常值”來講,destroy其實不是需要的,由於這些值被分派在常量段上。然則關於像類的對象或許構造體實例來講,假如不包管初始化和摧毀配對的話,是會湧現內存洩漏的。所以沒有特別斟酌的話,豈論內存中究竟是甚麼,包管initialize:和destroy配對會是一個好習氣。

指向數組的指針

在Swift中將一個數組作為參數傳遞到C API時,Swift曾經贊助我們完成了轉換,這在Apple的官方博客中有個很好的例子:


import Accelerate 
let a: [Float] = [1, 2, 3, 4] 
let b: [Float] = [0.5, 0.25, 0.125, 0.0625] 
var result: [Float] = [0, 0, 0, 0] 
vDSP_vadd(a, 1, b, 1, &result, 1, 4) 
// result now contains [1.5, 2.25, 3.125, 4.0625] 

關於普通的接收const數組的C API,其請求的類型為UnsafePointer,而非const的數組則對應UnsafeMutablePointer。應用時,關於const的參數,我們直接將Swift數組傳入(上例中的a和b);而關於可變的數組,在後面加上&後傳入便可(上例中的result)。

關於傳參,Swift停止了簡化,應用起來異常便利。然則假如我們想要應用指針來像之前用memory的方法直接操作數組的話,就須要借助一個特別的類型:UnsafeMutableBufferPointer。

Buffer Pointer是一段持續的內存的指針,平日用來表達像是數組或許字典如許的聚集類型。


var array = [1, 2, 3, 4, 5] 
var arrayPtr = UnsafeMutableBufferPointer<Int>(start: &array, count: array.count) 
// baseAddress 是第一個元素的指針 
var basePtr = arrayPtr.baseAddress as UnsafeMutablePointer<Int> 
basePtr.memory // 1 
basePtr.memory = 10 
basePtr.memory // 10 
//下一個元素 
var nextPtr = basePtr.successor() 
nextPtr.memory // 2 

指針操作和轉換

withUnsafePointer

下面我們說過,在Swift中不克不及像C裡那樣應用&符號直接獲得地址來停止操作。假如我們想對某個變量停止指針操作,我們可以借助withUnsafePointer這個幫助辦法。這個辦法接收兩個參數,第一個是 inout的隨意率性類型,第二個是一個閉包。Swift會將第一個輸出轉換為指針,然後將這個轉換後的Unsafe的指針作為參數,去挪用閉包。應用起來年夜概是這個模樣:

var test = 10 
test = withUnsafeMutablePointer(&test, { (ptr: UnsafeMutablePointer<Int>) -> Int in 
    ptr.memory += 1 
    return ptr.memory 
}) 
test // 11 

這裡其實我們做了和文章一開端的incrementor雷同的工作,差別在於不須要經由過程辦法的挪用來將值轉換為指針。這麼做的利益關於那些只會履行一次的指針操作來講是不言而喻的,可以將“我們就是想對這個指針做點事兒”這個意圖表達得加倍清楚明白。

unsafeBitCast

unsafeBitCast長短常風險的操作,它會將一個指針指向的內存強迫按位轉換為目的的類型。由於這類轉換是在Swift的類型治理以外停止的,是以編譯器沒法確保獲得的類型能否確切准確,你必需明白地曉得你在做甚麼。好比:

let arr = NSArray(object: "meow") 
let str = unsafeBitCast(CFArrayGetValueAtIndex(arr, 0), CFString.self) 
str // “meow” 

由於NSArray是可以寄存隨意率性NSObject對象的,當我們在應用CFArrayGetValueAtIndex從中取值的時刻,獲得的成果將是一個UnsafePointer<Void>。因為我們很明確個中寄存的是String對象,是以可以直接將其強迫轉換為CFString。

關於unsafeBitCast一種更罕見的應用場景是分歧類型的指針之間停止轉換。由於指針自己所占用的的年夜小是必定的,所以指針的類型停止轉換是不會出甚麼致命成績的。這在與一些C API協作時會很罕見。好比有許多C API請求的輸出是void *,對應到Swift中為UnsafePointer<Void>。我們可以經由過程上面如許的方法將隨意率性指針轉換為UnsafePointer。

var count = 100 
var voidPtr = withUnsafePointer(&count, { (a: UnsafePointer<Int>) -> UnsafePointer<Void> in 
    return unsafeBitCast(a, UnsafePointer<Void>.self) 
}) 
// voidPtr 是 UnsafePointer<Void>。相當於 C 中的 void * 
// 轉換回 UnsafePointer<Int> 
var intPtr = unsafeBitCast(voidPtr, UnsafePointer<Int>.self) 
intPtr.memory //100 

總結

Swift從設計下去說就是以平安作為主要准繩的,固然能夠有些煩瑣,然則照樣要重申在Swift中直接應用和操作指針應當作為最初的手腕,它們一直是沒法確保平安的。從傳統的C代碼和與之無縫合營的Objective-C代碼遷徙到Swift其實不是一件小工程,我們的代碼庫確定會時不時湧現一些和C協作的處所。我們固然可以選擇應用Swift重寫部門陳腐代碼,然則關於像是平安或許機能相當主要的部門,我們能夠除持續應用C API之外別無選擇。假如我們想要持續應用那些API的話,懂得一些根本的Swift指針操作和應用的常識會很有贊助。

關於新的代碼,盡可能防止應用Unsafe開首的類型,意味著可以免許多不用要的費事。Swift給開辟者帶來的最年夜利益是可讓我們用加倍先輩的編程思惟,停止更快和更專注的開辟。只要在尊敬這類思惟的條件下,我們能力更好地享用這門新說話帶來的各種優勢。明顯,這類思惟是不包含隨處應用 UnsafePointer的。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved