2017-07-24 123 views
3

更改在游乐场,下面的代码初始化数据使用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]。为什么价值在变化?

+0

这是确切的代码? 'data'是一个常量,所以你不能在这里重新分配它:'data = Data.init(...)' –

+0

是的,它是确切的代码。在Swift 3之前,原因是因为让我们只提到数据的引用,而不是数据本身。然而,在Swift 3之后,基础类被桥接到Swift结构。所以我不知道它为什么有效,但它仍然有效。 –

+0

@Hamish感谢有关MemoryLayout的信息。我真的不知道预期的行为是什么。理想情况下,代码将对应于数组中的数字(我认为)。我想了解更多关于基础框架的知识,为此,已经为每个基础课程制作了使用API​​的每个部分的游乐场。我已经把它交给了Data,正在通过初始化程序,到达了这个程序,并且现在处于亏损状态。如果你知道如何/为什么有人会使用这个初始值设定项,或者只是知道UnsafePointer,UnsafeBuffer等的一个很好的指南,我会非常感激。 –

回答

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;它必须被创建的),并Datainit(buffer:)构造新的实例与从给定的缓冲器点的字节呃。

如果您希望字节与数组中的元素1:1对应,您需要使每个元素的长度为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序列工作,你其实可以通过使用Datasequence of UInt8 initialiser简化初始化咯:

let data = Data(test) 
+0

说真的,谢谢。对数据(以及许多其他基础课程实际上)的信息很好地解释为最新,几乎不可能,所以这非常有用。 –

+0

@BrandonBradley乐于助人:) – Hamish