如何从UnsafeMutableRawPointer中获取字节?

29

如何从由C API(Core Audio等)传递给Swift函数的UnsafeMutableRawPointer指向的内存中访问字节(或Int16、浮点数等)。(UnsafeMutableRawPointer是Swift 3中新增的)


1
请问您能否提供一个最小化、完整化和可验证化的示例,以便我们使用一些具体的数据进行工作? - Alexander
3
Swift Evolution proposal for UnsafeRawPointer 可能是一个不错的起点 - 它提供了一个带有示例的 API 概述。 - Hamish
4个回答

50

load<T> 从内存中读取原始字节并构造出类型为T的值:

let ptr = ... // Unsafe[Mutable]RawPointer
let i16 = ptr.load(as: UInt16.self)

可选地在字节偏移处:

let i16 = ptr.load(fromByteOffset: 4, as: UInt16.self)

还有 assumingMemoryBound() 方法,它将从指向类型为 T 的内存中的 Unsafe[Mutable]RawPointer 转换为 Unsafe[Mutable]Pointer<T>

let i16 = ptr.assumingMemoryBound(to: UInt16.self).pointee

对于值的数组,您可以创建一个"缓冲指针":

let i16bufptr = UnsafeBufferPointer(start: ptr.assumingMemoryBound(to: UInt16.self), count: count)

一个缓冲指针可能已经足够满足您的需求,它可以像数组一样进行下标操作和枚举。如果需要,可以从缓冲指针创建一个数组:

let i16array = Array(i16bufptr)

正如@Hamish所说,更多的信息和细节可以在以下链接中找到:


谢谢!这对第一个元素有效。那UInt16(复数)怎么办,因为可能会有一组数据?(例如在Int16音频样本缓冲区中) - hotpaw2
ptr.assumingMemoryBound(to: Int16.self).pointee = Int16(foo()) ; ptr += 2 // 看起来也适用于按顺序分配数组元素 - hotpaw2
在填充缓冲区时,我收到了“无法通过下标赋值,下标只能用于获取”的错误。 - Necktwi
i16bufptr[0]=32 - Necktwi
@neckTwi:这个问题是关于读取由原始指针引用的字节。如果你想改变它,那么你需要使用 let i16bufptr = UnsafeMutableBufferPointer(...) - Martin R
显示剩余3条评论

4
这是一个Swift 4的示例,将字面量UInt8数组转换为UnsafeMutableRawPointer,然后再转换回UInt32数组。
static func unsafePointerTest() {
    //let a : [UInt8] = [0,0,0,4,0,0,0,8,0,0,0,12]
    let a : [UInt8] = [0x04, 0x00, 0x00, 0x00,
                       0x08, 0x00, 0x00, 0x00,
                       0x0C, 0x00, 0x00, 0x00] //little endian
    //0xFF, 0xF0, 0xF0, 0x12]  //317780223 = 12F0F0FF
    let b:UnsafeMutableRawPointer = UnsafeMutableRawPointer(mutating:a)
    let bTypedPtr = b.bindMemory(to: UInt32.self, capacity: a.count/4)
    let UInt32Buffer = UnsafeBufferPointer(start: bTypedPtr, count: a.count/4)
    let output = Array(UInt32Buffer)
    print(output)
}

3
创建Data对象。
init(bytesNoCopy bytes: UnsafeMutableRawPointer, count: Int, deallocator: Data.Deallocator)

这里缺少的一种重要方法是使用UnsafeMutableRawPointer初始化Data对象。然后可以将该数据对象用于其他计算。

public func base64(quality: Int32 = 67) -> String? {
    var size: Int32 = 0
    if let image = gdImageJpegPtr(internalImage, &size, quality) {
        // gdImageJpegPtr returns an UnsafeMutableRawPointer that is converted to a Data object
        let d = Data(bytesNoCopy: image, count: Int(size), deallocator: .none)
        return d.base64EncodedString()
    }
    return nil
}

这非常方便!我需要将dealloc更改为none,但这对我很有帮助。非常感谢。 - VaporwareWolf

2
这里提供关于Unsafe[Mutable]RawPointer到T/Unsafe[MutablePointer]转换的API文档:
/// Binds the allocated memory to type `T` and returns an
/// `UnsafePointer<T>` to the bound memory at `self`.
///
/// - Precondition: The memory is uninitialized.
/// - Postcondition: The memory is bound to 'T' starting at `self` continuing
///   through `self` + `count` * `MemoryLayout<T>.stride`
/// - Warning: Binding memory to a type is potentially undefined if the
///   memory is ever accessed as an unrelated type.
public func bindMemory<T>(to type: T.Type, capacity count: Int) -> UnsafePointer<T>

/// Converts from an `UnsafeRawPointer` to UnsafePointer<T> given that
/// the region of memory starting at `self` is already bound to type `T`.
///
/// - Precondition: The memory is bound to 'T' starting at `self` for some
///   unspecified capacity.
///
/// - Warning: Accessing memory via the returned pointer is undefined if the
///   if the memory has not been bound to `T`.
public func assumingMemoryBound<T>(to: T.Type) -> UnsafePointer<T>

/// Reads raw bytes from memory at `self + offset` and constructs a
/// value of type `T`.
///
/// - Precondition: The underlying pointer plus `offset` is properly
///   aligned for accessing `T`.
///
/// - Precondition: The memory is initialized to a value of some type, `U`,
///   such that `T` is layout compatible with `U`.
public func load<T>(fromByteOffset offset: Int = default, as type: T.Type) -> T

然后从Unsafe[MutablePointer]<T>转换为T可以使用pointeemove API进行转换。

/// Accesses the `Pointee` instance referenced by `self`.
///
/// - Precondition: the pointee has been initialized with an instance of
///   type `Pointee`.
public var pointee: Pointee { get }

/// Retrieves the `pointee`, returning the referenced memory to an
/// uninitialized state.
///
/// Equivalent to `{ defer { deinitialize() }; return pointee }()`, but
/// more efficient.
///
/// - Precondition: The pointee is initialized.
///
/// - Postcondition: The memory is uninitialized.
public func move() -> Pointee

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接