数据 <-> MutableRandomAccessSlice

Data <-> MutableRandomAccessSlice

我真的很纠结 someData[start...stop] returns 一个 MutableRandomAccessSlice。我的 someData 一开始是一个 let,那我为什么要一个 Mutable 东西呢?为什么我不只得到一个 RandomAccessSlice。不过,真正令人沮丧的是,它 returns 与原始来源相当 API 不兼容。对于 Data,我可以使用 .withUnsafeBytes,但对于这个后代则不然。以及如何将 Slice 变回 Data 也不清楚。没有采用其中之一的 init。

我可以使用 subdata(in:) 方法而不是下标,但是如果我只想要一个行为类似于原始集合的子集合表示,那么下标有什么意义。再者,subdata方法只能做开子范围,为什么下标既可以做闭也可以做开。这是他们还没有完全完成 Swift3 final 的东西吗?

请记住,您返回的 MutableRandomAccessSlicevalue 类型,而不是引用类型。只是表示你可以随意修改,但与你从中切出的东西无关:

let x = Data(bytes: [1,2,3]) // <010203>
var y = x[0...1]
y[0] = 2
x // <010203>

如果您查看 code,您会注意到其目的是 return 自定义切片类型:

public subscript(bounds: Range<Index>) -> MutableRandomAccessSlice<Data> {
    get {
        return MutableRandomAccessSlice(base: self, bounds: bounds)
    }
    set {
        // Ideally this would be:
        //   replaceBytes(in: bounds, with: newValue._base)
        // but we do not have access to _base due to 'internal' protection
        // TODO: Use a custom Slice type so we have access to the underlying data
        let arrayOfBytes = newValue.map { [=11=] }
        arrayOfBytes.withUnsafeBufferPointer {
            let otherData = Data(buffer: [=11=])
            replaceBytes(in: bounds, with: otherData)
        }
    }
}

也就是说,自定义切片对于采用数据的函数来说仍然是不可接受的。不过,这与其他类型一致,例如 Array,它切片为 ArraySlice,而 ArraySlice 不能传递到需要数组的地方。这是设计使然(出于同样的原因,也可能是为了数据)。关注的是切片 "pins" 支持它的所有内存。因此,如果您从一个兆字节数据中取出一个 3 字节的切片并将其存储在一个 ivar 中,那么整个兆字节都必须保留。理论(根据与我交谈过的 Swift 开发人员的说法)是数组可能很大,因此您需要小心地对它们进行切片,而字符串通常要小得多,因此将字符串切片为字符串是可以的.

根据我目前的经验,您通常需要 subdata(in:)。我对它的实验是它在速度上与切片非常相似,所以我相信它仍然是写时复制(但在我的初始测试中它似乎没有固定内存)。不过,到目前为止,我只在 Mac 上进行过测试。 iOS 设备上的性能差异可能更大。

根据 Rob 的评论,我刚刚添加了以下 pythonesque 下标扩展:

extension Data {
    subscript(start:Int?, stop:Int?) -> Data {
        var front = 0
        if let start = start {
            front = start < 0 ? Swift.max(self.count + start, 0) : Swift.min(start, self.count)
        }
        var back = self.count
        if let stop  = stop {
            back = stop < 0 ? Swift.max(self.count + stop, 0) : Swift.min(stop, self.count)
        }
        if front >= back {
            return Data()
        }
        let range = Range(front..<back)
        return self.subdata(in: range)
    }
}

这样我就可以了

let input = Data(bytes: [0x60, 0x0D, 0xF0, 0x0D])
input[nil, nil] // <600df00d>
input[1, 3]     // <0df0>
input[-2, nil]  // <f00d>
input[nil, -2]  // <600d>