2017-07-24 57 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