有没有办法在不复制的情况下将 "cast" 数据转换为 [UInt8](一个字节数组),或者是不安全的方法?

Is there a way to "cast" Data to [UInt8] (a byte array) without copying, or is Unsafe the way to go?

我知道我能做到

//Setup
let originalBytes: [UInt8] = [0x0, 0x1, 0x2]
let originalData = Data(originalBytes)

//This
let getByteArFromData: [UInt8] = [UInt8](originalData)

现在,我漂亮确定getByteArFromData将字节复制到新分配的内存中(我在这里错了吗?)。

随着数据规模的扩大(例如图像、编解码器操作等),这会变得 昂贵

我有动力问这个问题,因为我想通过类似数组的接口来操作数据,但据我所知,Unsafe API 是可行的方法。所以...

使用不安全 API 是唯一的方法吗?这是推荐的方法吗?

我也玩过一些指针运算(刚刚进入不安全 API,不确定如果我可以使用类似数组的接口是否应该投入时间)。

这是一些 playground 代码(如果您愿意,请评论并回答评论中的问题):

import Foundation

//MARK:- Setup

final class PlaygroundSwingSet
{
    //Squish unimportant setup code
    private let byteAr: [UInt8]
    private lazy var data: Data = {Data(byteAr)}()
    init(_ bytes: [UInt8]) {self.byteAr = bytes}

    func swing() -> Int
    {
        //Unsafe API stuff
        return data.withUnsafeBytes {(dataBufferPtr) -> (Int) in   //No need for [weak self], right?
            //I don't like how the pointers are only valid in this closure.
            //It adds an indent to my code to which my "code OCD" forces me to abstract out
            self.middleOut(dataBufferPtr)
        }
    }

    private func middleOut(_ dataBufferPtr: UnsafeRawBufferPointer) -> Int
    {
        //Yuck, I have to make sure count isn't 0 AND safely unwrap.
        //Why is .baseAddress even optional then?
        guard let dataPtr = dataBufferPtr.baseAddress?.assumingMemoryBound(to: UInt8.self), dataBufferPtr.count > 0 else {
            print("data is empty")
            return 0
        }
        let middishIndex = dataBufferPtr.count / 2 - 1

        //More yuck.
        print("truncated middle element's value is \(dataPtr.advanced(by: middishIndex).pointee)")
        print("this should yield the same thing: \((dataPtr + middishIndex).pointee)")
        return middishIndex
    }
}

//MARK:- Code Execution

let a = PlaygroundSwingSet([0x1, 0x2, 0x3, 0x4]).swing() //a = 1
//Console prints
/*
 truncated middle element's value is 3
 this should yield the same thing: 2
 */
let b = PlaygroundSwingSet([]).swing()  //b = 0
//Console prints
/*
 data is empty
 */

无需转换数据,因为它支持 subscript operator 访问给定索引处的 UInt8

顺便说一句,它也支持isEmpty,所以你不需要检查count是否为0。

var data = Data(repeating: 0, count: 5)
data.indices.forEach { data[[=10=]] = UInt8([=10=]) }
print(data[2])