Swift Playground中的数据值变化

时间:2017-07-24 20:38:52

标签: swift nsdata foundation

在游乐场中,以下代码使用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]。为什么价值在变化?

1 个答案:

答案 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