當前位置: 首頁>>編程示例 >>用法及示例精選 >>正文


Swift UnsafeMutablePointer用法及代碼示例

結構

UnsafeMutablePointer

用於訪問和操作特定類型數據的指針。

聲明

@frozen struct UnsafeMutablePointer<Pointee>

概述

您使用UnsafeMutablePointer 類型的實例來訪問內存中特定類型的數據。指針可以訪問的數據類型是指針的Pointee 類型。 UnsafeMutablePointer 不提供自動內存管理或對齊保證。您負責通過不安全的指針處理您使用的任何內存的生命周期,以避免泄漏或未定義的行為。

您手動管理的內存可以是特定類型的untypedbound。您使用UnsafeMutablePointer 類型來訪問和管理已綁定到特定類型的內存。

了解指針的內存狀態

UnsafeMutablePointer 實例引用的內存可以處於多種狀態之一。許多指針操作隻能應用於內存處於特定狀態的指針——您必須跟蹤正在使用的內存的狀態,並了解不同操作執行的對該狀態的更改。內存可以是無類型的和未初始化的,綁定到一個類型但未初始化,或者綁定到一個類型並初始化為一個值。最後,先前分配的內存可能已被釋放,留下現有指針引用未分配的內存。

未初始化的內存

剛剛通過類型化指針分配或已取消初始化的內存處於uninitialized 狀態。必須先初始化未初始化的內存,然後才能訪問它以進行讀取。

您可以使用 initialize(repeating:count:)initialize(from:count:)moveInitialize(from:count:) 之類的方法來用一個值或一係列值初始化指針所引用的內存。

初始化內存

Initialized 內存具有可以使用指針的 pointee 屬性或通過下標表示法讀取的值。在下麵的示例中,ptr 是一個指向內存的指針,初始化為 23


let ptr: UnsafeMutablePointer<Int> = ...
// ptr.pointee == 23
// ptr[0] == 23

以不同類型訪問指針的內存

通過UnsafeMutablePointer實例訪問內存時,Pointee類型必須與內存的綁定類型一致。如果您確實需要將綁定到一種類型的內存作為不同類型訪問,Swift 的指針類型提供了type-safe 方法來臨時或永久更改內存的綁定類型,或者直接從原始內存加載類型化實例。

分配有八字節內存的 UnsafeMutablePointer<UInt8> 實例 uint8Pointer 將用於以下示例。


var bytes: [UInt8] = [39, 77, 111, 111, 102, 33, 39, 0]
let uint8Pointer = UnsafeMutablePointer<UInt8>.allocate(capacity: 8)
uint8Pointer.initialize(from: &bytes, count: 8)

當您隻需要臨時以不同類型訪問指針的內存時,請使用withMemoryRebound(to:capacity:) 方法。例如,您可以使用此方法調用 API,該 API 需要指向與您的指針的 Pointee 布局兼容的不同類型的指針。下麵的代碼臨時將uint8Pointer引用的內存從UInt8重新綁定到Int8,以調用導入的Cstrlen函數。


// Imported from C
func strlen(_ __s: UnsafePointer<Int8>!) -> UInt


let length = uint8Pointer.withMemoryRebound(to: Int8.self, capacity: 8) {
    return strlen($0)
}
// length == 7

當您需要將內存永久重新綁定到不同類型時,首先獲取指向內存的原始指針,然後在原始指針上調用bindMemory(to:capacity:) 方法。以下示例將 uint8Pointer 引用的內存綁定到 UInt64 類型的一個實例:


let uint64Pointer = UnsafeMutableRawPointer(uint8Pointer)
                          .bindMemory(to: UInt64.self, capacity: 1)

在將 uint8Pointer 引用的內存重新綁定到 UInt64 後,作為 UInt8 實例訪問該指針的引用內存是未定義的。


var fullInteger = uint64Pointer.pointee          // OK
var firstByte = uint8Pointer.pointee             // undefined

或者,隻要綁定類型和目標類型是普通類型,您就可以訪問相同的內存作為不同類型,而無需通過非類型化內存訪問重新綁定。將指針轉換為 UnsafeMutableRawPointer 實例,然後使用原始指針的 load(fromByteOffset:as:)storeBytes(of:toByteOffset:as:) 方法讀取和寫入值。


let rawPointer = UnsafeMutableRawPointer(uint64Pointer)
let fullInteger = rawPointer.load(as: UInt64.self)   // OK
let firstByte = rawPointer.load(as: UInt8.self)      // OK

執行類型化指針算術

具有類型化指針的指針算術以指針的Pointee 類型的步長計數。當您添加到 UnsafeMutablePointer 實例或從 UnsafeMutablePointer 實例減去時,結果是相同類型的新指針,偏移量為 Pointee 類型的實例數。


// 'intPointer' points to memory initialized with [10, 20, 30, 40]
let intPointer: UnsafeMutablePointer<Int> = ...


// Load the first value in memory
let x = intPointer.pointee
// x == 10


// Load the third value in memory
let offsetPointer = intPointer + 2
let y = offsetPointer.pointee
// y == 30

您還可以使用下標表示法來訪問內存中特定偏移量的值。


let z = intPointer[2]
// z == 30

隱式轉換和橋接

當使用 UnsafeMutablePointer 參數調用函數或方法時,您可以傳遞該特定指針類型的實例或使用 Swift 的隱式橋接來傳遞兼容的指針。

例如,以下代碼示例中的 printInt(atAddress:) 函數需要一個 UnsafeMutablePointer<Int> 實例作為其第一個參數:


func printInt(atAddress p: UnsafeMutablePointer<Int>) {
    print(p.pointee)
}

與 Swift 中的典型情況一樣,您可以使用 UnsafeMutablePointer 實例調用 printInt(atAddress:) 函數。此示例將 intPointer (指向 Int 值的可變指針)傳遞給 print(address:)


printInt(atAddress: intPointer)
// Prints "42"

或者,您可以使用 Swift 的 implicit bridging 將指針傳遞給實例或數組的元素。以下示例使用 inout 語法傳遞指向 value 變量的指針:


var value: Int = 23
printInt(atAddress: &value)
// Prints "23"

使用 inout 語法傳遞數組時,會隱式創建指向數組元素的可變指針。此示例在調用 printInt(atAddress:) 時使用隱式橋接將指針傳遞給 numbers 的元素。


var numbers = [5, 10, 15, 20]
printInt(atAddress: &numbers)
// Prints "5"

無論你以哪種方式調用 printInt(atAddress:) ,Swift 的類型安全保證你隻能傳遞一個指向函數所需類型的指針——在本例中,是一個指向 Int 的指針。

可用版本

iOS 8.0+, iPadOS 8.0+, macOS 10.10+, Mac Catalyst 13.0+, tvOS 9.0+, watchOS 2.0+

相關用法


注:本文由純淨天空篩選整理自apple.com大神的英文原創作品 UnsafeMutablePointer。非經特殊聲明,原始代碼版權歸原作者所有,本譯文未經允許或授權,請勿轉載或複製。