Swift 游乐场中的数据值发生变化
Data Value Changing in Swift Playground
在 playground 中,以下代码使用 UnsafeBufferPointer 初始化数据,如 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] 生成不同的值。为什么价值会改变?
MemoryLayout.size(ofValue: test)
等同于 MemoryLayout<[Int]>.size
(该参数仅用作推断通用占位符类型的一种方式)。它没有给你数组缓冲区的大小,而是给你 Array
结构 本身 的大小,它目前是 1 个字(在 64 位机器上是 8 个字节) ) 的大小,因为元素是间接持有的。
因此您构造的Data
实例只包含8个字节,因此访问data[8]
将读取越界垃圾;这就是为什么你会得到意想不到的结果。这种越界访问实际上 cause a runtime error in Swift 4(从 Xcode 9 beta 4 附带的版本开始)。
但是忽略所有这些,首先使用 UnsafeRawPointer(test)
是 未定义的行为,因为它使用的是指向缓冲区的指针,该缓冲区仅在初始化调用。 Swift 仅保证自动生成的指针参数(例如,将数组传递给常量指针参数时)在给定函数调用期间有效(参见 Swift 团队的博客 post Interacting with C Pointers).
如果您只想将数组缓冲区的字节转储到 Data
实例中,您只需:
let test = Array(0 ..< 10)
let data = test.withUnsafeBufferPointer(Data.init)
// or let data = test.withUnsafeBufferPointer { Data(buffer: [=10=]) }
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(_:)
to get an immutable buffer pointer view onto the array's buffer (and if it's not native, e.g wrapping an NSArray
; it will have to be created), and Data
's init(buffer:)
使用给定缓冲区指针中的字节构造一个新实例。
如果希望字节与数组中的元素一一对应,则需要使每个元素的长度为 1 个字节。
例如,以 [UInt8]
:
开头
let test = [UInt8](0 ..< 10)
let data = test.withUnsafeBufferPointer(Data.init)
print(data as NSData) // <00010203 04050607 0809>
print(data[8]) // 8
并且因为您现在正在处理 UInt8
序列,您实际上可以通过使用 Data
的 sequence of UInt8
initialiser:
稍微简化初始化
let data = Data(test)
在 playground 中,以下代码使用 UnsafeBufferPointer 初始化数据,如 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] 生成不同的值。为什么价值会改变?
MemoryLayout.size(ofValue: test)
等同于 MemoryLayout<[Int]>.size
(该参数仅用作推断通用占位符类型的一种方式)。它没有给你数组缓冲区的大小,而是给你 Array
结构 本身 的大小,它目前是 1 个字(在 64 位机器上是 8 个字节) ) 的大小,因为元素是间接持有的。
因此您构造的Data
实例只包含8个字节,因此访问data[8]
将读取越界垃圾;这就是为什么你会得到意想不到的结果。这种越界访问实际上 cause a runtime error in Swift 4(从 Xcode 9 beta 4 附带的版本开始)。
但是忽略所有这些,首先使用 UnsafeRawPointer(test)
是 未定义的行为,因为它使用的是指向缓冲区的指针,该缓冲区仅在初始化调用。 Swift 仅保证自动生成的指针参数(例如,将数组传递给常量指针参数时)在给定函数调用期间有效(参见 Swift 团队的博客 post Interacting with C Pointers).
如果您只想将数组缓冲区的字节转储到 Data
实例中,您只需:
let test = Array(0 ..< 10)
let data = test.withUnsafeBufferPointer(Data.init)
// or let data = test.withUnsafeBufferPointer { Data(buffer: [=10=]) }
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(_:)
to get an immutable buffer pointer view onto the array's buffer (and if it's not native, e.g wrapping an NSArray
; it will have to be created), and Data
's init(buffer:)
使用给定缓冲区指针中的字节构造一个新实例。
如果希望字节与数组中的元素一一对应,则需要使每个元素的长度为 1 个字节。
例如,以 [UInt8]
:
let test = [UInt8](0 ..< 10)
let data = test.withUnsafeBufferPointer(Data.init)
print(data as NSData) // <00010203 04050607 0809>
print(data[8]) // 8
并且因为您现在正在处理 UInt8
序列,您实际上可以通过使用 Data
的 sequence of UInt8
initialiser:
let data = Data(test)