日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

Swift?指針底層探索分析_Swift

作者:文乙 ? 更新時間: 2022-11-14 編程語言

概要

主要內容:

指針的認識

指針的常見綁定

1. 指針的認識

指針分為兩類,指定數據類型和未指定數據類型

區別:

1.1 指定類型指針

代碼:

運行結果:

說明:

  • 指針的內存需要自己管理,需要手動開辟空間和釋放空間
  • 存儲數據時,需要移動一定的字節大小
  • 移動通過advanced實現
  • 存儲數據通過storeBytes實現
  • 取出數據通過load實現

1.2 未指定類型指針

代碼:

<!--定義-->
@inlinable public func withUnsafePointer<T, Result>(to value: inout T, _ body: (UnsafePointer<T>) throws -> Result) rethrows -> Result
<!--使用1-->
var age = 10
let p = withUnsafePointer(to: &age) { $0 }
print(p)
<!--使用2-->
withUnsafePointer(to: &age){print($0)}
<!--使用3-->
//其中p1的類型是 UnsafePointer<Int>
let p1 = withUnsafePointer(to: &age) { ptr in
    return ptr
}

說明:

  • 對于withUnsafePointer的定義,我們可以看到閉包中返回的結果就是這個函數返回的結果
  • 因此我們在使用這個指針時,就可以通過閉包返回數據來決定拿到的結果
  • 所以可以看到我們可以有兩種方式,1)直接返回指針;2)返回具體的數據

訪問屬性:

直接修改:

直接在閉包中計算后將結果返回給屬性

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

間接修改:

var age = 10
//分配容量大小,為8字節
let ptr = UnsafeMutablePointer<Int>.allocate(capacity: 1)
//初始化
ptr.initialize(to: age)
ptr.deinitialize(count: 1)
ptr.pointee += 12
print(ptr.pointee)
//釋放
ptr.deallocate()

說明:

  • 返回指針,指針拿到pointee來進行修改

1.3 訪問結構體實例對象

結構體:

struct CJLTeacher {
    var age = 10
    var height = 1.85
}
var t = CJLTeacher()

指針處理:

//分配兩個CJLTeacher大小的空間
let ptr = UnsafeMutablePointer<CJLTeacher>.allocate(capacity: 2)
//初始化第一個空間
ptr.initialize(to: CJLTeacher())
//移動,初始化第2個空間
ptr.successor().initialize(to: CJLTeacher(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()

說明:

  • 直接通過下標來獲取
  • 通過指針偏移來獲取
  • 通過successor()偏移一步來獲取,它可以通用在指定指針類型和未指定指針類型

2. 指針的常見綁定

2.1 指針與內存空間的綁定(指向)(bindMemory)

將指針指向某個內存空間,也就是綁定到這個內存空間上

定義:

struct HeapObject {
    var kind: Int
    var strongRef: UInt32
    var unownedRef: UInt32
}
class CJLTeacher{
    var age = 18
}
var t = CJLTeacher()

綁定:

//將t綁定到結構體內存中
//1、獲取實例變量的內存地址,聲明成了非托管對象
/*
 通過Unmanaged指定內存管理,類似于OC與CF的交互方式(所有權的轉換 __bridge)
 - passUnretained 不增加引用計數,即不需要獲取所有權
 - passRetained 增加引用計數,即需要獲取所有權
 - toOpaque 不透明的指針
 */
let ptr = Unmanaged.passUnretained(t as AnyObject).toOpaque()
//2、綁定到結構體內存,返回值是UnsafeMutablePointer<T>
/*
 - bindMemory 更改當前 UnsafeMutableRawPointer 的指針類型,綁定到具體的類型值
    - 如果沒有綁定,則綁定
    - 如果已經綁定,則重定向到 HeapObject類型上
 */
let heapObject = ptr.bindMemory(to: HeapObject.self, capacity: 1)
//3、訪問成員變量
print(heapObject.pointee.kind)
print(heapObject.pointee.strongRef)
print(heapObject.pointee.unownedRef)

說明:

  • 首先創建一個指針,指針指向CJLTeacher
  • 通過bindMemeory將指針綁定到結構體HeapObject中
  • 接下來就可以通過這個指針來訪問內存數據了

2.2 元組指針類型轉換(假定內存綁定assumingMemoryBound)

元組和指針指向內存的數據類型不一樣,就需要使用假定內存綁定

代碼:

var tul = (10, 20)
//UnsafePointer<T>
func testPointer(_ p : UnsafePointer<Int>){
    print(p)
}
withUnsafePointer(to: &tul) { (tulPtr: UnsafePointer<(Int, Int)>) in
    //不能使用bindMemory,因為已經綁定到具體的內存中了
    //使用assumingMemoryBound,假定內存綁定,目的是告訴編譯器ptr已經綁定過Int類型了,不需要再檢查memory綁定
    testPointer(UnsafeRawPointer(tulPtr).assumingMemoryBound(to: Int.self))
}

說明:

  • testPointe需要傳入的是一個泛型為Int的指針
  • 我們此時想要傳入一個元組,元組類型為(Int, Int),與Int類型不一樣
  • 因此無法通過memoryBind來直接指向Int內存
  • 所以就需要通過assumingMemoryBound(to: Int.self)來指向
  • 這是因為假定內存綁定是假綁定,不需要進行嚴格的類型檢查

舉例:獲取結構體的屬性的指針

struct HeapObject {
    var strongRef: UInt32 = 10
    var unownedRef: UInt32 = 20
}
func testPointer(_ p: UnsafePointer<Int>){
   print(p)
}
//實例化
var  t = HeapObject()
//獲取結構體屬性的指針傳入函數
withUnsafePointer(to: &t) { (ptr: UnsafePointer<HeapObject>) in
    //1. 獲取變量
    let strongRef = UnsafeRawPointer(ptr) + MemoryLayout<HeapObject>.offset(of: \HeapObject.strongRef)!
    //2. 傳遞strongRef屬性的值
    testPointer(strongRef.assumingMemoryBound(to: Int.self))
}

說明:

  • 通過地址偏移拿到結構體中的strngRef變量
  • 之后也是通過假定綁定將其轉換為一個指針(從Uint32到Int)

2.3 通過 withMemoryRebound 臨時綁定內存類型

問題:

代碼實現:

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

說明:

  • 此處可以看到ptr的指針泛型為Int,而testPointer的參數指針泛型為Int64,所以并不能直接傳遞
  • 而這個指針我們只是作為參數傳遞,所以就可以臨時綁定一下
  • 實現方式就是ptr.withMemoryRebound(to: Int64.self, capacity: 1)
  • 在出了這個作用域后,ptr仍然是Int類型

3、總結

  • 指針類型分兩種
  • typed pointer 指定數據類型指針,即 UnsafePointer< T > + unsafeMutablePointer
  • raw pointer 未指定數據類型的指針(原生指針) ,即UnsafeRawPointer + unsafeMutableRawPointer

指針的內存管理需要手動管理

假定內存綁定和內存綁定的區別

需要注意對于指針類型指針,可以通過指針偏移來偏移內存大小,而對于未指定類型的指針,只能通過內存偏移來偏移內存大小

將一個指針綁定到內存中,其實就是指向到這個內存空間

三種綁定的區別

  • 綁定:bindMemory(to: Capacity:): 更改內存綁定的類型,如果之前沒有綁定,那么就是首次綁定,如果綁定過了,會被重新綁定為該類型
  • 假定綁定:assumingMemoryBound假定內存綁定,這里就是告訴編譯器:我的類型就是這個,你不要檢查我了,其實際類型還是原來的類型
  • 臨時綁定:withMemoryRebound: 臨時更改內存綁定類型

原文鏈接:https://juejin.cn/post/7146419541059305502

欄目分類
最近更新