在游乐场中,以下代码使用UnsafeBufferPointer初始化Data,如Apple Foundation Documentation
中所述let data = Data()
let test = Array(0..<10)
let pointer = UnsafeRawPointer(test).assumingMemoryBound(to: UInt8.self)
data = Data.init(buffer: UnsafeBufferPointer(start: pointer, count: MemoryLayout.size(ofValue: test)))
data[8]
多次运行此程序会为数据生成不同的值[8]。为什么价值在变化?
答案 0 :(得分:1)
MemoryLayout.size(ofValue: test)
等同于MemoryLayout<[Int]>.size
(该参数仅用作推断通用占位符类型的方式)。它没有给你数组缓冲区的大小,而是给你Array
struct 本身的大小,它当前是1个字(64位机器上8个字节)的大小,因为元素是间接持有的。
因此,您构造的Data
实例仅保存8个字节,因此访问data[8]
将读取超出范围的垃圾;这就是为什么你会得到意想不到的结果。这个越界访问实际上是cause a runtime error in Swift 4(从Xcode 9 beta 4附带的版本开始)。
但是忽略所有这些,使用UnsafeRawPointer(test)
开始是未定义的行为,因为它使用指向缓冲区的指针,该缓冲区仅在初始化调用的持续时间内有效。 Swift只保证自动生成的指针参数(例如,当将数组传递给常量指针参数时)在给定函数调用的持续时间内有效(参见Swift团队在Interacting with C Pointers上的博客文章)。
如果您只想将数组缓冲区的字节转储到Data
实例中,您只需要:
let test = Array(0 ..< 10)
let data = test.withUnsafeBufferPointer(Data.init)
// or let data = test.withUnsafeBufferPointer { Data(buffer: $0) }
print(data as NSData) // bridge to NSData to get a full print-out of bytes
// <00000000 00000000
// 01000000 00000000
// 02000000 00000000
// 03000000 00000000
// 04000000 00000000
// 05000000 00000000
// 06000000 00000000
// 07000000 00000000
// 08000000 00000000
// 09000000 00000000>
print(data[8]) // 1
(64位小端机)
使用withUnsafeBufferPointer(_:)
获取不可变缓冲区指针视图到数组的缓冲区(如果它不是本机的,例如包装NSArray
;它必须被创建),{{1} } init(buffer:)
使用给定缓冲区指针中的字节构造一个新实例。
如果希望字节与数组中的元素1:1对应,则需要使每个元素的长度为1个字节。
例如,以Data
:
[UInt8]
由于您现在使用的是let test = [UInt8](0 ..< 10)
let data = test.withUnsafeBufferPointer(Data.init)
print(data as NSData) // <00010203 04050607 0809>
print(data[8]) // 8
序列,因此您可以使用UInt8
的{{3}}来轻微简化初始化:
Data