Swift指針

Swift中的指針分為兩類:
typed pointer 指定數(shù)據(jù)類型指針,即 UnsafePointer<T>,其中T表示泛型;
raw pointer 未指定數(shù)據(jù)類型的指針(原生指針) ,即UnsafeRawPointer。
OC的指針對比如下:

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

其中,T表示泛型,對照上文,OC指針表示,如:NSObjct *objc = [NSObjct alloc] init],void *objc = [NSObjct alloc] init]

一、type pointer

獲取基本數(shù)據(jù)類型的地址可通過withUnsafePointer(to:)方法獲取,例如:

/**
 value(T):
 body(Result): 閉包表達(dá)式,通過rethrows重新拋出Result(即閉包表達(dá)式產(chǎn)生的結(jié)果)。
 方法:
 @inlinable public func withUnsafePointer<T, Result>
 (to value: T, _ body: (Swift.UnsafePointer<T>) throws -> Result) 
 rethrows -> Result {}
 */

var age = 18
let p = withUnsafePointer(to: &age) { ptr in
    return ptr }
print(p)

//withUnsafePointer: 方法中的閉包屬于單一表達(dá)式,因此可以省略參數(shù)、返回值,
//直接使用$0,$0等價于ptr,表示第一個參數(shù),$1表示第二個參數(shù)
//簡寫為: withUnsafePointer(to: &age){print($0)}

//訪問指針的值,可以直接通過:pointee屬性
print(p.pointee)

p的類型是 UnsafePointer<Int>

image.png

1.1 修改指針值

修改指針指向的值,有2種方式,間接修改 & 直接修改。

var age = 18
//1、間接修改
age = withUnsafePointer(to: &age) { p in
    //返回Int整型值
    return p.pointee + 10
}
print("1、間接修改:\(age)")


//2.1、直接修改
withUnsafeMutablePointer(to: &age) { p in
    p.pointee += 10
}
print("2.1、直接修改:\(age)")


/**
 2.2 直接修改,通過 allocate 創(chuàng)建 UnsafeMutablePointer
     ①,initialize 與 deinitialize是成對的
     ②,deinitialize中的count與申請時的capacity需要一致
     ③,需要deallocate
 */
//分配容量大小,為8字節(jié)
let p = UnsafeMutablePointer<Int>.allocate(capacity: 1)
//初始化
p.initialize(to: age)
p.deinitialize(count: 1)

p.pointee += 10
print("2.2、直接修改 p:\(p.pointee)")
print("2.2、直接修改 age:\(age)")

//釋放
p.deallocate()

控制臺打?。?/p>

1、間接修改:28
2.1、直接修改:38
2.2、直接修改 p:48
2.2、直接修改 age:38

問題:為什么最后兩次打印的值不一樣,因?yàn)橹羔樞薷闹?,未賦值給age。

二、raw pointer

raw pointer也叫做原生指針,就是指未指定數(shù)據(jù)類型的指針。需要注意,raw pointer 需要手動管理 指針的內(nèi)存,所以指針在使用完需要手動釋放。

//定義一個未知類型的指針:本質(zhì)是分配32字節(jié)大小的空間,指定對齊方式是8字節(jié)對齊
let p = UnsafeMutableRawPointer.allocate(byteCount: 32, alignment: 8)

/**
 存值1:讀取數(shù)據(jù)時有問題,原因是因?yàn)樽x取時指定了每次讀取的大小。
 但是存儲是直接在8字節(jié)的p中存儲了i+1,即可以理解為并沒有指定存儲時的內(nèi)存大小
 */
//for i in 0..<4 {
//    p.storeBytes(of: i + 1, as: Int.self)
//}

//存值2
for i in 0..<4 {
    //指定當(dāng)前移動的步數(shù),即i * 8
    p.advanced(by: i * 8).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)")
}

//使用完需要手動釋放
p.deallocate()

三、指針應(yīng)用

3.1 訪問結(jié)構(gòu)體
struct Animal{
    var age: Int = 8
    var height:Float = 1.75
}

然后,使用UnsafeMutablePointer創(chuàng)建指針,訪問結(jié)構(gòu)體對象t

// 分配2個Animal大小的空間
let p = UnsafeMutablePointer<Animal>.allocate(capacity: 2)
// 初始化第一個空間
p.initialize(to: Animal())
// 移動,初始化第2個空間
p.successor().initialize(to: Animal(age: 10, height: 1.55))
//異常:p.advanced(by: MemoryLayout<Animal>.stride).initialize(to:  Animal(age: 20, height: 1.80))
//正常:p.advanced(by: 1).initialize(to:  Animal(age: 20, height: 1.80))

//訪問指針
//方式1:下標(biāo)訪問
print(p[0])
print(p[1])

//方式2:內(nèi)存平移
print(p.pointee)
print((p+1).pointee)

//方式3:successor()
print(p.pointee)
print(p.successor().pointee)   //successor 往前移動

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

注意:通過advanced(by: MemoryLayout<Animal>.stride)賦值不行,但是advanced(by: 1)可以,why?
advanced(by: MemoryLayout<Animal>.stride)的移動步長是類Animal實(shí)例的大小 16字節(jié),而advanced(by: 1)是移動步長為1。
關(guān)鍵 在于這句代碼 let ptr = UnsafeMutablePointer<Animal>.allocate(capacity: 2),此時我們是知道ptr的具體類型的,就是指向Animal的指針
所以:在確定指針的類型后,通過步長的移動+1,就表示移動了那個類的實(shí)例大小空間+1。

3.2 實(shí)例對象綁定到struct內(nèi)存
struct HeapObject {
    var kind: Int   //測試:Int, UnsafeRawPointer
    var strongRef: UInt32
    var unownedRef: UInt32
}

class Animal{
    var age = 18
}

var t = Animal()

Animal實(shí)例對象t綁定到結(jié)構(gòu)體HeapObject中:

//將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 p = Unmanaged.passUnretained(t as AnyObject).toOpaque()

/*
 2、綁定到結(jié)構(gòu)體內(nèi)存,返回值是 UnsafeMutablePointer<T>
 - bindMemory 更改當(dāng)前 UnsafeMutableRawPointer 的指針類型,綁定到具體的類型值
 - 如果沒有綁定,則綁定
 - 如果已經(jīng)綁定,則重定向到 HeapObject類型上
 */
let heapObject = p.bindMemory(to: HeapObject.self, capacity: 1)
//3、訪問成員變量
//print(heapObject.pointee.kind)
print(heapObject.pointee)

這時打印的kind是數(shù)值,把Kind的類型改為 UnsafeRawPointer,就可以打印地址了。

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

Swift底層對應(yīng)的class結(jié)構(gòu)

struct 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
}

然后,把kind綁定到類結(jié)構(gòu)

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

運(yùn)行結(jié)果:

HeapObject(kind: 0x0000000100008190, strongRef: 3, unownedRef: 0)
swift_class(kind: 0x0000000100008158, superClass: 0x00000001f49631a8, cachedata1: 0x00000001893f0e60, cachedata2: 0x0000802000000000, data: 0x00000001005169c2, flags: 2, instanceAddressOffset: 0, instanceSize: 24, flinstanceAlignMask: 7, reserved: 0, classSize: 136, classAddressOffset: 16, description: 0x0000000100003c90)
3.3 元組指針類型轉(zhuǎn)換

代碼:

var tul = (10, 20)

//UnsafePointer<T>
func testPointer(_ p : UnsafePointer<Int>){
    print(p)
    print("第一個值:\(p.pointee),第二個值:\((p+1).pointee)")
}

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))
}

上面是將元組tul的指針類型 UnsafePointer<(Int, Int)>)轉(zhuǎn)換成了UnsafePointer<Int>。
也可以直接告訴編譯器轉(zhuǎn)換成具體的類型:

func testPointer(_ p: UnsafeRawPointer){
    p.assumingMemoryBound(to: Int.self)
}
3.4 獲取結(jié)構(gòu)體的成員變量的指針
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))
}

通過withUnsafePointert綁定到結(jié)構(gòu)體HeapObject內(nèi)存中,然后通過 MemoryLayout<HeapObject>.offset()內(nèi)存平移獲取結(jié)構(gòu)體成員變量strongRef,最后通過assumingMemoryBound進(jìn)行內(nèi)存的綁定。
assumingMemoryBound:假定內(nèi)存綁定,就是告訴編譯器,我的類型就是這個,不用檢查了,但實(shí)際類型不變。

3.5 臨時綁定內(nèi)存類型
var age = 10

func testPointer(_ p: UnsafePointer<Int64>){
   print(p)
}
//異常:參考
withUnsafePointer(to: &age) { (ptr: UnsafePointer<Int>) in
    //Cannot convert value of type 'UnsafePointer<Int>' to expected argument type 'UnsafePointer<Int64>'
    //testPointer(ptr)  //報(bào)錯:指針類型不一致
}

let ptr = withUnsafePointer(to: &age) {$0}
//通過withMemoryRebound臨時綁定內(nèi)存類型
ptr.withMemoryRebound(to: Int64.self, capacity: 1) { (ptr: UnsafePointer<Int64>)  in
    testPointer(ptr)
}
總結(jié)

withMemoryRebound:臨時更改內(nèi)存綁定類型;
bindMemory(to: Capacity:):更改內(nèi)存綁定的類型,如果之前沒有綁定,那么就是首次綁定,如果綁定過了,會被重新綁定為該類型;
assumingMemoryBound:假定內(nèi)存綁定,就是告訴編譯器,我的類型就是這個,不用檢查了,其實(shí)際類型不變。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一、指針 ?? 1、指針類型 ??Swift中的指針分為兩類:指定數(shù)據(jù)類型的指針(typed pointer);未...
    spyn_n閱讀 1,099評論 1 4
  • Swift 指針 前言 指針,作為編程中最重要的概念,一直存在于各大語言中,下面我們就來探索一下Swift中的指針...
    just東東閱讀 998評論 0 3
  • 指針不是安全的 1.野指針。指針指向?qū)ο筢尫?,指針變?yōu)橐爸羔?2.指針超出內(nèi)存空間邊界訪問。如數(shù)組越界 3.原?指...
    張?zhí)煊頮bba7閱讀 834評論 0 1
  • 前言 本篇文章主要講解一下Swift中的指針,以及相關(guān)的應(yīng)用場景,指針也是面試官經(jīng)常問到的知識點(diǎn),希望大家能夠掌握...
    深圳_你要的昵稱閱讀 3,621評論 0 11
  • 指針分類: raw pointer:未指定數(shù)據(jù)類型的指針(原生指針) typed pointer:指定數(shù)據(jù)類型的指...
    BBLv閱讀 717評論 0 1

友情鏈接更多精彩內(nèi)容