Swift進(jìn)階03:指針

第三節(jié)課:指針

指針

swift中的指針分為兩類

  • typed pointer 指定數(shù)據(jù)類型指針坯癣,即 UnsafePointer<T>,其中T表示泛型

  • raw pointer 未指定數(shù)據(jù)類型的指針(原生指針) 茬腿,即UnsafeRawPointer

swift與OC指針對比如下:

Swift OC 說明
unsafePointer<T> const T * 指針及所指向的內(nèi)容都不可變
unsafeMutablePointer T * 指針及其所指向的內(nèi)存內(nèi)容均可變
unsafeRawPointer const void * 指針指向未知類型
unsafeMutableRawPointer void * 指針指向未知類型

原生指針(raw pointer)

原生指針:是指未指定數(shù)據(jù)類型的指針,有以下說明

  • 對于指針的內(nèi)存管理是需要手動管理的

  • 指針在使用完需要手動釋放

我們可以看下下面的代碼夫凸,看看結(jié)果如何?

//原生指針
//對于指針的內(nèi)存管理是需要手動管理的
//定義一個(gè)未知類型的指針:本質(zhì)是分配32字節(jié)大小的空間,指定對齊方式是8字節(jié)對齊
let p = UnsafeMutableRawPointer.allocate(byteCount: 32, alignment: 8)

//存儲
for i in 0..<4 {
    p.storeBytes(of: i + 1, as: Int.self)
}
//讀取
for i in 0..<4 {
    //p是當(dāng)前內(nèi)存的首地址,通過內(nèi)存平移來獲取值
    let value = p.load(fromByteOffset: i * 8, as: Int.self)
    print("index: \(i), value: \(value)")
}

//使用完成需要dealloc迷殿,即需要手動釋放
p.deallocate()
指針01.png

通過運(yùn)行發(fā)現(xiàn),在讀取數(shù)據(jù)時(shí)有問題咖杂,原因是因?yàn)樽x取時(shí)指定了每次讀取的大小庆寺,但是存儲是直接在8字節(jié)的p中存儲了i+1,即可以理解為并沒有指定存儲時(shí)的內(nèi)存大小诉字。

通過修改advanced(by:)指定存儲時(shí)的步長

p.advanced(by: i * 8).storeBytes(of: i + 1, as: Int.self)
指針02.png

typed pointer

查看withUnsafePointer(to:的定義中懦尝,第二個(gè)參數(shù)傳入的是閉包表達(dá)式,然后通過rethrows重新拋出Result(即閉包表達(dá)式產(chǎn)生的結(jié)果)了壤圃,所以可以將閉包表達(dá)式進(jìn)行簡寫(簡寫參數(shù)陵霉、返回值),其中$0表示第一個(gè)參數(shù)伍绳,$1表示第二個(gè)參數(shù)踊挠,以此類推

<!--定義-->
@inlinable public func withUnsafePointer<T, Result>(to value: inout T, _ body: (UnsafePointer<T>) throws -> Result) rethrows -> Result

<!--使用1-->
//其中p1的類型是 UnsafePointer<Int>
let p1 = withUnsafePointer(to: &age) { ptr in
    return ptr
}

<!--使用2-->
var age = 10
let p = withUnsafePointer(to: &age) { $0 }
print(p)

<!--使用3-->
withUnsafePointer(to: &age){$0}

由于withUnsafePointer方法中的閉包屬于單一表達(dá)式,因此可以省略參數(shù)墨叛、返回值止毕,直接使用$0$0等價(jià)于ptr

訪問屬性

可以通過指針的pointee屬性訪問變量值漠趁,如下所示

var age = 10
let p = withUnsafePointer(to: &age) { $0 }
print(p.pointee)

<--輸出結(jié)果-->
10

改變屬性

改變變量值的方式有兩種扁凛,一種是間接修改,一種是直接修改

間接修改:需要在閉包中直接通過ptr.pointee修改并返回闯传。類似于C語言中
char *p = “HZM” 中的 *p谨朝,訪問HZM通過是 *p來訪問

var age = 10
age = withUnsafePointer(to: &age) { ptr in
    //返回Int整型值
    return ptr.pointee + 18
}
print(age)

直接修改:可以通過withUnsafeMutablePointer方法

var age = 10
withUnsafeMutablePointer(to: &age) { ptr in
    ptr.pointee += 12
}

還有一種直接修改的方式:通過allocate創(chuàng)建UnsafeMutablePointer

  • initialize 與 deinitialize是成對的
  • deinitialize中的count與申請時(shí)的capacity需要一致
  • 需要deallocate
var age = 10
//分配容量大小,為8字節(jié)
let ptr = UnsafeMutablePointer<Int>.allocate(capacity: 1)
//初始化
ptr.initialize(to: age)
ptr.deinitialize(count: 1)

ptr.pointee += 12
print(ptr.pointee)

//釋放
ptr.deallocate()

指針實(shí)戰(zhàn)應(yīng)用

1.訪問結(jié)構(gòu)體實(shí)例對象

struct HZMTeacher {
    var age = 10
    var height = 1.86
}
var t = HZMTeacher()

使用UnsafeMutablePointer創(chuàng)建指針甥绿,并通過指針訪問HZMTeacher實(shí)例對象字币,有以下三種方式:

  • 方式一:下標(biāo)訪問
  • 方式二:內(nèi)存平移
  • 方式三:successor
//分配兩個(gè)HZMTeacher大小的空間
let ptr = UnsafeMutablePointer<HZMTeacher>.allocate(capacity: 2)
//初始化第一個(gè)空間
ptr.initialize(to: HZMTeacher())
//移動,初始化第2個(gè)空間
ptr.successor().initialize(to: HZMTeacher(age: 20, height: 1.75))

//訪問方式一
print(ptr[0])
print(ptr[1])

//訪問方式二
print(ptr.pointee)
print((ptr+1).pointee)

//訪問方式三
print(ptr.pointee)
//successor 往前移動
print(ptr.successor().pointee)

//必須和分配是一致的
ptr.deinitialize(count: 2)
//釋放
ptr.deallocate()

需要注意的是共缕,第二個(gè)空間的初始化不能通過advanced(by:MemoryLayout<HZMTeacher>.stride)去訪問洗出,否則取出結(jié)果是有問題

指針03.png

可以通過ptr + 1或者successor() 或者advanced(by: 1)

<!--第2個(gè)初始化 方式一-->
(ptr + 1).initialize(to: HZMTeacher(age: 20, height: 1.75))

<!--第2個(gè)初始化 方式二-->
ptr.successor().initialize(to: HZMTeacher(age: 20, height: 1.75))

<!--第2個(gè)初始化 方式三-->
ptr.advanced(by: 1).initialize(to:  HZMTeacher(age: 20, height: 1.75))

我們可以對比下上面的原生指針

let p = UnsafeMutableRawPointer.allocate(byteCount: 32, alignment: 8)

//存儲
for i in 0..<4 {
    //指定當(dāng)前移動的步數(shù),即i * 8
    p.advanced(by: i * 8).storeBytes(of: i + 1, as: Int.self)
}

這里p使用advanced(by: i * 8)图谷,是因?yàn)榇藭r(shí)并不知道 p 的具體類型翩活,必須指定每次移動的步長

let ptr = UnsafeMutablePointer<HZMTeacher>.allocate(capacity: 2)
//初始化第一個(gè)空間
ptr.initialize(to: HZMTeacher())
//移動,初始化第2個(gè)空間
ptr.advanced(by: 1).initialize(to:  HZMTeacher(age: 20, height: 1.75))

這里的ptr如果使用advanced(by: MemoryLayout<HZMTeacher>.stride)即16*16字節(jié)大斜愎蟆(對應(yīng)i * 8)菠镇,此時(shí)獲取的結(jié)果是有問題的,由于這里知道具體的類型承璃,所以只需要標(biāo)識指針前進(jìn) 幾步即可利耍,即advanced(by: 1)

2.實(shí)例對象綁定到struct內(nèi)存

使用如下代碼

struct HeapObject {
    var kind: Int
    var strongRef: UInt32
    var unownedRef: UInt32
}

class HZMTeacher{
    var age = 18
}

var t = HZMTeacher()

將t綁定進(jìn)我們結(jié)構(gòu)體內(nèi)存中

  1. 獲取實(shí)例變量內(nèi)存地址
  2. 綁定到結(jié)構(gòu)體內(nèi)存,返回值是UnsafeMutablePointer<T>
  3. 訪問成員變量 pointee.kind
//將t綁定到結(jié)構(gòu)體內(nèi)存中
//1、獲取實(shí)例變量的內(nèi)存地址,聲明成了非托管對象
/*
 通過Unmanaged指定內(nèi)存管理隘梨,類似于OC與CF的交互方式(所有權(quán)的轉(zhuǎn)換 __bridge)
 - passUnretained 不增加引用計(jì)數(shù)程癌,即不需要獲取所有權(quán)
 - passRetained 增加引用計(jì)數(shù),即需要獲取所有權(quán)
 - toOpaque 不透明的指針
 */
let ptr = Unmanaged.passUnretained(t as AnyObject).toOpaque()

//2轴猎、綁定到結(jié)構(gòu)體內(nèi)存,返回值是UnsafeMutablePointer<T>
/*
 - bindMemory 更改當(dāng)前 UnsafeMutableRawPointer 的指針類型席楚,綁定到具體的類型值
    - 如果沒有綁定,則綁定
    - 如果已經(jīng)綁定税稼,則重定向到 HeapObject類型上
 */
let heapObject = ptr.bindMemory(to: HeapObject.self, capacity: 1)

//3烦秩、訪問成員變量
print(heapObject.pointee.kind)
print(heapObject.pointee.strongRef)
print(heapObject.pointee.unownedRef)
指針04.png
  • create\copy 需要使用retain

  • 不需要獲取所有權(quán) 使用unretain

  • 將kind的類型改成UnsafeRawPointer,kind的輸出就是地址了

指針05.png

綁定到類結(jié)構(gòu)

將swift中的類結(jié)構(gòu)定義成一個(gè)結(jié)構(gòu)體

struct HZM_swift_class {
    var kind: UnsafeRawPointer
    var superClass: UnsafeRawPointer
    var cachedata1: UnsafeRawPointer
    var cachedata2: UnsafeRawPointer
    var data: UnsafeRawPointer
    var flags: UInt32
    var instanceAddressOffset: UInt32
    var instanceSize: UInt32
    var flinstanceAlignMask: UInt16
    var reserved: UInt16
    var classSize: UInt32
    var classAddressOffset: UInt32
    var description: UnsafeRawPointer
}

將t改成綁定到HZM_swift_class

//1郎仆、綁定到HZM_swift_class
let metaPtr = heapObject.pointee.kind.bindMemory(to: HZM_swift_class.self, capacity: 1)
//2只祠、訪問
print(metaPtr.pointee)

運(yùn)行結(jié)果如下,其本質(zhì)原因是因?yàn)?metaPtrHZM_swift_class的類結(jié)構(gòu)是一樣的

指針06.png

3.元組指針類型轉(zhuǎn)換

var tul = (10, 20)

//UnsafePointer<T>
func testPointer(_ p : UnsafePointer<Int>){
    print(p)
}

withUnsafePointer(to: &tul) { (tulPtr: UnsafePointer<(Int, Int)>) in
    //不能使用bindMemory扰肌,因?yàn)橐呀?jīng)綁定到具體的內(nèi)存中了
    //使用assumingMemoryBound抛寝,假定內(nèi)存綁定,目的是告訴編譯器ptr已經(jīng)綁定過Int類型了曙旭,不需要再檢查memory綁定
    testPointer(UnsafeRawPointer(tulPtr).assumingMemoryBound(to: Int.self))
}

或者直接告訴編譯器轉(zhuǎn)換成具體的類型

func testPointer(_ p: UnsafeRawPointer){
    p.assumingMemoryBound(to: Int.self)
}

4.如何獲取結(jié)構(gòu)體的屬性的指針

1盗舰、定義實(shí)例變量
2、獲取實(shí)例變量的地址桂躏,并將strongRef的屬性值傳遞給函數(shù)

struct HeapObject {
    var strongRef: UInt32 = 10
    var unownedRef: UInt32 = 20
}

func testPointer(_ p: UnsafePointer<Int>){
   print(p)
}
//實(shí)例化
var  t = HeapObject()
//獲取結(jié)構(gòu)體屬性的指針傳入函數(shù)
withUnsafePointer(to: &t) { (ptr: UnsafePointer<HeapObject>) in
    //獲取變量
    let strongRef = UnsafeRawPointer(ptr) + MemoryLayout<HeapObject>.offset(of: \HeapObject.strongRef)!
    //傳遞strongRef屬性的值
    testPointer(strongRef.assumingMemoryBound(to: Int.self))
}

5.通過 withMemoryRebound 臨時(shí)綁定內(nèi)存類型

如果方法的類型與傳入?yún)?shù)的類型不一致钻趋,會報(bào)錯(cuò)

指針07.png

通過withMemoryRebound臨時(shí)綁定內(nèi)存類型,在作用域外還是原類型

var age = 10
func testPointer(_ p: UnsafePointer<Int64>){
   print(p)
}
let ptr = withUnsafePointer(to: &age) {$0}
ptr.withMemoryRebound(to: Int64.self, capacity: 1) { (ptr: UnsafePointer<Int64>)  in
    testPointer(ptr)
}

總結(jié)

指針類型分兩種
typed pointer 指定數(shù)據(jù)類型指針剂习,即 UnsafePointer<T> + unsafeMutablePointer
raw pointer 未指定數(shù)據(jù)類型的指針(原生指針) 蛮位,即UnsafeRawPointer + UnsafeMutableRawPointer

withMemoryRebound: 臨時(shí)更改內(nèi)存綁定類型

bindMemory(to: Capacity:): 更改內(nèi)存綁定的類型,如果之前沒有綁定鳞绕,那么就是首次綁定失仁,如果綁定過了,會被重新綁定為該類型

assumingMemoryBound假定內(nèi)存綁定们何,這里就是告訴編譯器:我的類型就是這個(gè)萄焦,你不要檢查我了,其實(shí)際類型還是原來的類型

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市冤竹,隨后出現(xiàn)的幾起案子拂封,更是在濱河造成了極大的恐慌,老刑警劉巖贴见,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件烘苹,死亡現(xiàn)場離奇詭異躲株,居然都是意外死亡片部,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來档悠,“玉大人廊鸥,你說我怎么就攤上這事∠剿” “怎么了惰说?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長缘回。 經(jīng)常有香客問我吆视,道長,這世上最難降的妖魔是什么酥宴? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任啦吧,我火速辦了婚禮,結(jié)果婚禮上拙寡,老公的妹妹穿的比我還像新娘授滓。我一直安慰自己,他們只是感情好肆糕,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布般堆。 她就那樣靜靜地躺著,像睡著了一般诚啃。 火紅的嫁衣襯著肌膚如雪淮摔。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天始赎,我揣著相機(jī)與錄音噩咪,去河邊找鬼。 笑死极阅,一個(gè)胖子當(dāng)著我的面吹牛胃碾,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播筋搏,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼仆百,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了奔脐?” 一聲冷哼從身側(cè)響起俄周,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎髓迎,沒想到半個(gè)月后峦朗,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡排龄,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年波势,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,809評論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡尺铣,死狀恐怖拴曲,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情凛忿,我是刑警寧澤澈灼,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布,位于F島的核電站店溢,受9級特大地震影響叁熔,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜床牧,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一者疤、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧叠赦,春花似錦驹马、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至册踩,卻和暖如春泳姐,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背暂吉。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工胖秒, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人慕的。 一個(gè)月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓阎肝,卻偏偏與公主長得像,于是被迫代替她去往敵國和親肮街。 傳聞我的和親對象是個(gè)殘疾皇子风题,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,724評論 2 351

推薦閱讀更多精彩內(nèi)容