当前位置: 首页>>编程示例 >>用法及示例精选 >>正文


Swift UnsafePointer用法及代码示例

结构

UnsafePointer

用于访问特定类型数据的指针。

声明

@frozen struct UnsafePointer<Pointee>

概述

您使用UnsafePointer 类型的实例来访问内存中特定类型的数据。指针可以访问的数据类型是指针的Pointee 类型。 UnsafePointer 不提供自动内存管理或对齐保证。您负责通过不安全的指针处理您使用的任何内存的生命周期,以避免泄漏或未定义的行为。

您手动管理的内存可以是特定类型的untypedbound。您使用UnsafePointer 类型来访问和管理已绑定到特定类型的内存。

了解指针的内存状态

UnsafePointer 实例引用的内存可以处于多种状态之一。许多指针操作只能应用于内存处于特定状态的指针——您必须跟踪正在使用的内存的状态,并了解不同操作执行的对该状态的更改。内存可以是无类型的和未初始化的,绑定到一个类型但未初始化,或者绑定到一个类型并初始化为一个值。最后,先前分配的内存可能已被释放,留下现有指针引用未分配的内存。

未初始化的内存

刚刚通过类型化指针分配或已取消初始化的内存处于uninitialized 状态。必须先初始化未初始化的内存,然后才能访问它以进行读取。

初始化内存

Initialized 内存具有可以使用指针的 pointee 属性或通过下标表示法读取的值。在下面的示例中,ptr 是一个指向内存的指针,初始化为 23


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

以不同类型访问指针的内存

通过UnsafePointer实例访问内存时,Pointee类型必须与内存的绑定类型一致。如果您确实需要将绑定到一种类型的内存作为不同类型访问,Swift 的指针类型提供了type-safe 方法来临时或永久更改内存的绑定类型,或者直接从原始内存加载类型化实例。

分配有八字节内存的 UnsafePointer<UInt8> 实例 uint8Pointer 将用于以下示例。


let uint8Pointer: UnsafePointer<UInt8> = fetchEightBytes()

当您只需要临时以不同类型访问指针的内存时,请使用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 = UnsafeRawPointer(uint8Pointer)
                          .bindMemory(to: UInt64.self, capacity: 1)

在将 uint8Pointer 引用的内存重新绑定到 UInt64 后,作为 UInt8 实例访问该指针的引用内存是未定义的。


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

或者,只要绑定类型和目标类型是普通类型,您就可以访问相同的内存作为不同类型,而无需通过非类型化内存访问重新绑定。将指针转换为 UnsafeRawPointer 实例,然后使用原始指针的 load(fromByteOffset:as:) 方法读取值。


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

执行类型化指针算术

具有类型化指针的指针算术以指针的Pointee 类型的步长计数。当您添加到 UnsafePointer 实例或从 UnsafePointer 实例减去时,结果是相同类型的新指针,偏移量为 Pointee 类型的实例数。


// 'intPointer' points to memory initialized with [10, 20, 30, 40]
let intPointer: UnsafePointer<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

隐式转换和桥接

当使用 UnsafePointer 参数调用函数或方法时,您可以传递该特定指针类型的实例,传递兼容指针类型的实例,或使用 Swift 的隐式桥接来传递兼容指针。

例如,以下代码示例中的 printInt(atAddress:) 函数需要一个 UnsafePointer<Int> 实例作为其第一个参数:


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

与 Swift 中的典型情况一样,您可以使用 UnsafePointer 实例调用 printInt(atAddress:) 函数。此示例将 intPointer (指向 Int 值的指针)传递给 print(address:)


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

因为可变类型指针在作为参数传递时可以隐式转换为具有相同Pointee 类型的不可变指针,所以您也可以使用UnsafeMutablePointer 实例调用printInt(atAddress:)


let mutableIntPointer = UnsafeMutablePointer(mutating: intPointer)
printInt(atAddress: mutableIntPointer)
// Prints "42"

或者,您可以使用 Swift 的 implicit bridging 将指针传递给实例或数组的元素。以下示例使用 inout 语法传递指向 value 变量的指针:


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

当您将数组作为参数传递时,会隐式创建指向数组元素的不可变指针。此示例在调用 printInt(atAddress:) 时使用隐式桥接将指针传递给 numbers 的元素。


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

您还可以使用 inout 语法将可变指针传递给数组的元素。因为printInt(atAddress:) 需要一个不可变指针,虽然这在语法上是有效的,但它不是必需的。


var mutableNumbers = numbers
printInt(atAddress: &mutableNumbers)

无论你以哪种方式调用 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大神的英文原创作品 UnsafePointer。非经特殊声明,原始代码版权归原作者所有,本译文未经允许或授权,请勿转载或复制。