Swift中指針

Swift中有4個指針類型:UnsafePointerUnsafeMutablePointerUnsafeRawPointerUnsafeMutableRawPointer,通過指針可以直接修改指向的內存數據,下面簡單介紹下它們的幾個常見用法。

一、從已有的變量獲取指針
  1. 獲取指向變量或者指向對象的指針、讀寫指向變量的值的方式如下:
var age = 10

// 通過withUnsafeXXXXPointer創建指向變量或對象的指針
func testGetPointer() {
    // 1.指向值類型變量時
    var ptr1 = withUnsafePointer(to: &age, { $0 })
    var ptr2 = withUnsafeMutablePointer(to: &age, { $0 })
//    ptr1.pointee = 15 // ptr1不可變,這裏報錯
    ptr2.pointee = 20
    print("ptr1=\(ptr1.pointee), ptr2=\(ptr2.pointee)") // 都是20
    // 使用原始指針
    var ptr3 = withUnsafePointer(to: &age, { UnsafeRawPointer($0) })
    var ptr4 = withUnsafeMutablePointer(to: &age, { UnsafeMutableRawPointer($0) })
    ptr4.storeBytes(of: 50, as: Int.self)
    print("ptr3=\(ptr3.load(as: Int.self)), ptr4=\(ptr4.load(as: Int.self))") // 都是50
    
    // 2.指向對象時
    var hba = HBA(no: "no119", name: "name120")
    let ptr5 = withUnsafePointer(to: &hba, { $0 })
    let ptr6 = withUnsafeMutablePointer(to: &hba, { $0 })
    ptr6.pointee.no = "no911"
    print("no=\(ptr5.pointee.no),name=\(ptr5.pointee.name)") // no=no911,name=name120
    
    // 原始指針
    var ptr7 = withUnsafePointer(to: &hba, { UnsafeRawPointer($0) })
//    var ptr8 = withUnsafeMutablePointer(to: &hba, { UnsafeMutableRawPointer($0) })
    // ptr7、ptr8的值是hba指針的地址值,要訪問到hba對象的堆空間地址值還需要如下操作
    // 根據bitPattern參數(堆空間地址)初始化一個指針
    var heapPtr = UnsafeRawPointer(bitPattern: ptr7.load(as: UInt.self))!
    // 將這個堆空間指針轉換類型後就可以很方便得訪問屬性
    let hba2 = unsafeBitCast(heapPtr, to: HBA.self)
    print("hba2==", hba2.name)
}
  1. 作爲函數參數時,可變類型的指針UnsafeMutablePointerUnsafeRawPointer與inout參數效果類似,都可以修改指向的值,不同之處在於inout參數可以直接改。
func testPointer()  {
    useinout(&age)
    usePointer1(&age)
    usePointer2(&age)
    usePointer3(&age)
    usePointer4(&age)
}

func useinout(_ ptr: inout Int) {
    print("inout-ptr前:\(ptr)")
    ptr = 90
    print("inout-ptr後:\(ptr)")
}

// UnsafePointer不可修改指向內存
func usePointer1(_ ptr: UnsafePointer<Int>) { // 相當於const int *
    print("ptr.pointee:\(ptr.pointee)")
}

// UnsafeMutablePointer可修改指向內容
func usePointer2(_ ptr: UnsafeMutablePointer<Int>) {// 相當於int *
    print("ptr.pointee前\(ptr.pointee)")
    ptr.pointee = 20
    print("ptr.pointee後\(ptr.pointee)")
}

// 不可修改的原始指針
func usePointer3(_ rawptr: UnsafeRawPointer) { // 相當於const void *
    print("rawptr:\(rawptr.load(as: Int.self))")
}

// UnsafeMutableRawPointer可修改指向內容的原始指針
func usePointer4(_ rawptr: UnsafeMutableRawPointer) {// 相當於void *
    print("rawptr.pointee前\(rawptr.load(as: Int.self))")
    rawptr.storeBytes(of: 19, as: Int.self)
    print("rawptr.pointee後\(rawptr.load(as: Int.self))") // 19
}
二、創建新的指針

可通過4種方式創建新的指針:

  • UnsafeRawPointer(bitPattern: 內存地址)
  • malloc(字節數)
  • 可變原始指針,UnsafeMutableRawPointer.allocate(byteCount:字節數, alignment: 1)
  • 可變指針,UnsafeMutablePointer<泛型類型>.allocate(capacity: 泛型類型個數)
// 新創建指針
func testNewPointer() {
    // 1.直接傳內存地址來創建一個原始指針
    var ptr = UnsafeRawPointer(bitPattern: 0x100001234)
    // 2.通過malloc來創建一個可變原始指針(可以存值)
    var ptr2 = malloc(16)
    // 修改
    ptr2?.storeBytes(of: 118, as: Int.self)
    ptr2?.storeBytes(of: 220, toByteOffset: 8, as: Int.self)
    // 讀取
    print((ptr2?.load(as: Int.self))!) // 118
    print((ptr2?.load(fromByteOffset: 8, as: Int.self))!) //220
    
    // 3.通過可變原始指針分配、修改、讀取內存
    var ptr3 = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)
    ptr3.storeBytes(of: 30, as: Int.self)
    ptr3.advanced(by: 8).storeBytes(of: 33, as: Int.self)
    
    print(ptr3.load(as: Int.self)) // 30
    print(ptr3.advanced(by: 8).load(as: Int.self)) //33
    
    // 4.通過可變指針分配、修改、讀取內存
    var ptr4 = UnsafeMutablePointer<Int>.allocate(capacity: 3)
    ptr4.initialize(to: 11)
    ptr4.successor().initialize(to: 22)
    ptr4.successor().successor().initialize(to: 33)
    
    print("第1個int = \(ptr4.pointee), 第2個int = \((ptr4 + 1).pointee), 第3個int = \((ptr4 + 2).pointee)")
    print("第1個int = \(ptr4[0]), 第2個int = \(ptr4[1]), 第3個int = \(ptr4[2])")
    
    ptr4.deinitialize(count: 3)
    ptr4.deallocate()
}
三、指針類型轉換

上述指針類型之間可以進行轉換,如下:

// 指針之間的轉換
func testPointerTransfer() {
    let ptr = UnsafeMutableRawPointer.allocate(byteCount: 16, alignment: 1)
    // 1. 通過assumingMemoryBound來轉換
    ptr.assumingMemoryBound(to: Int.self).pointee = 11
    (ptr + 8).assumingMemoryBound(to: Double.self).pointee = 22
    
    // 2.通過unsafeBitCast進行忽略類型的強制轉換
    // 轉換爲UnsafePointer<Int>類型後通過pointee訪問
    let ptr_trannsfer = unsafeBitCast(ptr, to: UnsafePointer<Int>.self)
    print(ptr_trannsfer.pointee)
    let ptr_trannsfer2 = unsafeBitCast(ptr + 8, to: UnsafePointer<Double>.self)
    print(ptr_trannsfer2.pointee)
    
    // 3.UnsafeMutableRawPointer可以通過bindMemory方法轉換爲UnsafeMutablePointer
    let ptr222 = (ptr + 8).bindMemory(to: Double.self, capacity: 1)
    ptr222.pointee = 33.0
    print(ptr222.pointee)
    
    ptr.deallocate()
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章