【发布时间】:2020-11-03 18:52:00
【问题描述】:
动机
据我所知,Data 是一个抽象字节缓冲区的结构。它引用内存中的物理区域,换句话说:连续的字节数。现在我想在内存中有效地存储多个值(作为原始数据),其中的值并非都是同一类型。
我在这里对高效的定义 ≔ 存储所有这些值,没有任何未使用的缓冲区/间隙字节。
将原始数据存储在内存中
let a: UInt8 = 39
let b: Int32 = -20001
let string: String = "How awesome is this data?!"
现在我想将所有这些值的数据顺序存储在内存中,没有任何类型信息。
let data = [a.asData, b.asData, string.asData].concatenated()
假设.asData 属性将每个实例的字节表示检索为[UInt8] 数组,然后将它们包装在Data 实例中。 concetenated() 方法然后将这 3 个 Data 实例连接到单个 Data 实例,如下所示:
extension Collection where Element == Data {
func concatenated() -> Data {
reduce(into: Data()) { (result, nextDataChunk) in
result.append(nextDataChunk)
}
}
}
从内存中读回数据到各自的类型中
假设这一切都很好,我现在有一个 Data 实例,我想从中恢复 3 个原始值(及其原始类型)。我就是这样做的:
var cursor = 0
let a: UInt8 = data.withUnsafeBytes { pointer in
pointer.load(fromByteOffset: cursor, as: UInt8.self)
}
cursor += MemoryLayout<UInt8>.size // +1
let b: Int32 = data.withUnsafeBytes { pointer in
pointer.load(fromByteOffset: cursor, as: Int32.self)
}
cursor += MemoryLayout<Int32>.size // +4
let string: String = data.withUnsafeBytes { pointer in
pointer.load(fromByteOffset: cursor, as: String.self)
}
cursor += MemoryLayout<String>.size // +16
问题
问题是这会引发运行时错误:
致命错误:从未对齐的原始指针加载
我知道为什么:
Int32 的对齐方式为 4(因为它有 4 个字节长)。换句话说:当使用原始指针读取数据时,Int32 的第一个字节必须位于 4 的倍数的索引处。但由于第一个值仅是 UInt8,@ 的数据字节987654336@ 从索引 1 开始,不是 4 的倍数。因此,我得到了错误。
我的问题是这样的:
-
我能否以某种方式使用代表不同类型实例的原始
Data来重新创建此类实例而不会出现对齐错误?怎么样? -
如果这是不可能的,有没有办法在连接它们时自动正确对齐
Data块?
【问题讨论】:
标签: ios swift pointers nsdata foundation