雖然在Swift中是不推薦直接訪問指針的购啄,但是Swift還是保留了訪問指針的方法,當(dāng)然,完全拋棄C一套的東西還是相當(dāng)困難的咖摹。
UnsafePointer
在C中,我們有如下的方法:
void method(const int *num) {
printf("%d",*num);
}
其對(duì)應(yīng)的Swift方法是:
func method(_ num: UnsafePointer<CInt>) {
print(num.pointee)
}
其中CInt
對(duì)應(yīng)的就是C中的Int
類型难述,類似的還有CBool
和CChar
等萤晴。C方法中num
是不可變的,那么對(duì)應(yīng)的UnsafePointer
也是不可變的胁后。Swift中可變的指針類型是UnsafeMutablePointer
店读。
這里通過(guò)指針直接去操作內(nèi)存是一種很不安全的行為,所以Swift的指針類型前加了一個(gè)Unsafe
來(lái)強(qiáng)調(diào)攀芯。
C指針的內(nèi)存管理
在C中聲明了一個(gè)內(nèi)存塊在不用的時(shí)候要記得釋放屯断。在Swift中依然如此。
來(lái)看一個(gè)例子:
let size = 10
var pointer = UnsafeMutablePointer<Int>.allocate(capacity: size) // 申請(qǐng)內(nèi)存
for idx in 0..<size {
pointer.advanced(by: idx).pointee = idx
}
pointer.advanced(by: 9) // 輸出內(nèi)存地址
pointer.deallocate(capacity: size) // 釋放內(nèi)存
我們?cè)赟wift申請(qǐng)一塊內(nèi)存可以用allocate
方法侣诺,但是UnsafeMutablePointer
并不能自動(dòng)進(jìn)行內(nèi)存管理殖演,需要我們手動(dòng)去釋放,有點(diǎn)像ARC紧武。
UnsafeMutablePointer
提供了一個(gè)方法 advanced(by:)
來(lái)遍歷內(nèi)存剃氧,這個(gè)方法返回了另一個(gè) UnsafeMutablePointer
func advanced(by n: Int) -> UnsafeMutablePointer<Pointee>
上面的代碼用這個(gè)方法為指針賦值。
在申請(qǐng)到內(nèi)存之后我們還可以用initialize
方法來(lái)創(chuàng)建這個(gè)指針阻星。
錯(cuò)誤的例子:
class MyClass {
var a = 1
deinit {
print("deinit")
}
}
var pointer: UnsafeMutablePointer<MyClass>!
pointer = UnsafeMutablePointer<MyClass>.allocate(capacity: 1)
pointer.initialize(to: MyClass())
print(pointer.pointee.a) // 1
pointer = nil
雖然這里設(shè)置pointer
為nil
朋鞍,但是內(nèi)存并不會(huì)被釋放已添,就如前面所說(shuō)的,UnsafeMutablePointer
時(shí)不會(huì)自動(dòng)進(jìn)行內(nèi)存管理的滥酥,這就造成了內(nèi)存泄露更舞。正確的做法是為 pointer
加入 deinitialize
和 deallocate
,它們分別會(huì)釋放指針指向的內(nèi)存的對(duì)象以及指針自己本身:
var pointer: UnsafeMutablePointer<MyClass>!
pointer = UnsafeMutablePointer<MyClass>.allocate(capacity: 1)
pointer.initialize(to: MyClass())
print(pointer.pointee.a)
pointer.deinitialize()
pointer.deallocate(capacity: 1)
pointer = nil
// 輸出:
// 1
// deinit
如果我們?cè)?deallocate
之后再去訪問 pointer
或者再次調(diào)用 deallocate
的話坎吻,迎接我們的自然是崩潰缆蝉。
數(shù)組指針
對(duì)于上面用advanced(by:)
來(lái)遍歷內(nèi)存并且賦值,Swift 還有一個(gè) UnsafeBufferPointer
的結(jié)構(gòu)體來(lái)更方便的實(shí)現(xiàn)這個(gè)需求瘦真。這個(gè)結(jié)構(gòu)體是一個(gè)Swift數(shù)組和指針的橋梁刊头。如果我們使用 UnsafePointer
來(lái)作為變量從而調(diào)用創(chuàng)建函數(shù)創(chuàng)建一個(gè) UnsafeBufferPointer
,我們將能夠使用大多數(shù)的Swift原生的數(shù)組(Array)方法诸尽,因?yàn)?UnsafeBufferPointer
遵守并實(shí)現(xiàn)了 Collections
原杂, Indexable
和 RandomAccessCollection
協(xié)議。所以我們可以像這樣遍歷內(nèi)存:
var pointerBuffer = UnsafeBufferPointer(start: pointer, count: size)
pointerBuffer.forEach{
print("\($0)") // 輸出 0 到 9
}
// 這段代碼放在pointer被deallocate之前
當(dāng)我們提到 UnsafeBufferPointer
的是一個(gè)Swift中數(shù)組的橋梁的時(shí)候您机,這也意味著我們很容易使用 UnsafeBufferPointer
來(lái)調(diào)用一個(gè)已經(jīng)存在的數(shù)組穿肄,比如說(shuō)下面這個(gè)例子:
var a = [1, 2, 3]
a.withUnsafeBufferPointer {
print($0.baseAddress) // 輸出數(shù)組地址
}
如果我們只打印$0
那么我們將看到類似這樣的輸出UnsafeBufferPointer(start: 0x00006080000710e0, count: 3)
那么我們也可以換一種方式來(lái)打印數(shù)組的地址:
var pbArray = UnsafeBufferPointer(start: &a, count: a.count)
if let pointer = pbArray.baseAddress {
pointer
}
在處理C的API時(shí),我們就可以這樣去將C的方法轉(zhuǎn)換成Swift的形式际看,以及管理指針和內(nèi)存咸产,但是內(nèi)存管理一定要嚴(yán)謹(jǐn),否則仲闽,崩潰隨時(shí)可能發(fā)生脑溢。