2015-08-31 92 views
5

我發現SwiftNSData是一種不聖潔的沮喪婚姻。每當我處理這件事情時,我發現我覺得所有新發現的Swift安全都會出現在窗外。崩潰的數量(無益的痕跡)沒有幫助。使用Swift從NSData中獲取數據

所以,我瞭解到,我可以做這樣的事情下面避免可怕的UnsafeMutablePointer東西:

var bytes = [UInt8](count: 15, repeatedValue: 0) 
anNSData.getBytes(&bytes, length=15) 

我還發現,我可以直接解壓縮到奇異值:

var u32:UInt32 = 0 
anNSData.getBytes(&u32, length=4) 

這導致了兩個中間問題:

1)有什麼我可以使用,比那裏的硬編碼常量更可靠。如果這是C,我只使用sizeof。但我想我讀了,也許我應該使用strideof而不是sizeof?這不會在[UInt8]的工作,是嗎?

2)文檔(對於Swift)說這個參數應該是_ buffer: UnsafeMutablePointer<Void>。那麼這是如何工作的?我幸運嗎?爲什麼我想要這樣做,而不是更原生/託管的[Uint8]結構?我想知道如果UnsafeMutablePointer是一個協議,但它是一個結構。

通過直接讀取數值(而不是數組),我想也許我可以嘗試另一種結構。我有一個6字節結構,看起來像:

struct TreeDescription : Hashable { 
    var id:UInt32 = 0x00000000 
    var channel:UInt8 = 0x00 
    var rssi:UInt8 = 0x00 

    var hashValue:Int { 
     return Int(self.id) 
    } 
} 

哪些實際工作(認爲它沒有,但最終做一個乾淨的,其取得了一些崩潰消失後)!

var tree = TreeDescription() 
anNSData.getBytes(&newTree, length: 6) 

但是這讓我擔心結構包裝的細節?爲什麼這個工作?我該怎麼擔心這樣做?

對我來說,這一切都感覺非常C-ISH。我認爲Swift把C從ObjectiveC中拿出來了。

回答

2

你可能想看看RawData這是一個非常新的東西,這個人只是嘗試了一下這個想法,所以不要認爲它測試得很好或者什麼都不是,有些功能甚至還沒有實現。它基本上是一個Swift-y包裝(你猜對了)原始數據,一系列字節。

使用這個擴展,你可以用NSData實例初始化它:

extension RawData { 
    convenience init(data: NSData) { 
     self.init(UnsafeMutableBufferPointer(start: UnsafeMutablePointer(data.bytes), count: data.length)) 
    } 
} 

You'de可以調用它像這樣:

let data = "Hello, data!".dataUsingEncoding(NSASCIIStringEncoding)! 

let rawData = RawData(data: data) 

編輯:爲了回答您的問題:

問題是數據可能很大,非常大。你通常不想複製大量東西,因爲空間是有價值的。 [UInt8]值的數組與NSData實例之間的區別在於,數組每次都被複制,您將其賦給函數 - >新副本,您執行賦值 - >新副本。對於大數據來說這不是很理想。

1)如果你想最天然,安全的方式,沒有任何第三方庫,提到的,你可以這樣做:

let data = UnsafeMutableBufferPointer(start: UnsafeMutablePointer(data.bytes), count: data.length) 

(我知道這聽起來不是很安全,但相信我)。您可以像使用普通陣列一樣使用它:

let data = "Hello, data!".dataUsingEncoding(NSASCIIStringEncoding)! 

let bytes = UnsafeMutableBufferPointer(start: UnsafeMutablePointer<UInt8>(data.bytes), count: data.length) 

for byte in bytes {} 
bytes.indexOf(0) 
bytes.maxElement() 

並且它不會在您傳遞數據時複製數據。

2)UnsafeMutablePointer<Void>確實非常類C,在這種情況下,它表示指針序列中的起始值(也稱爲基)。 Void類型也來自C,這意味着指針不知道它存儲的是什麼類型的值。你可以將所有類型的指針轉​​換爲你期待的類型,如下所示:UnsafeMutablePointer<Int>(yourVoidPointer)(這不應該崩潰)。如前所述,您可以使用UnsafeMutableBufferPointer將其用作您的類型的集合。 UnsafeMutableBufferPointer只是您的基指針和長度的包裝(這解釋了我使用的初始化程序)。

將數據直接解碼到您的結構中的方法確實有效,即使在編譯時間之後,結構的屬性也是正確的順序,並且結構的大小正好是它存儲的屬性的總和。對於像你這樣簡單的數據來說,這是完全正確的。有一個替代方案:使用NSCoding協議。優點:更安全。缺點:你必須繼承NSObject。我認爲你應該堅持現在的做法。我要改變的一件事是將結構解碼放入結構本身,並使用sizeof。有這樣的:

struct TreeDescription { 
    var id:UInt32 = 0x00000000 
    var channel:UInt8 = 0x00 
    var rssi:UInt8 = 0x00 

    init(data: NSData) { 
     data.getBytes(&self, length: sizeof(TreeDescription)) 
    } 
} 

另一個編輯:您可以隨時與方法memory返回類型爲T得到一個Unsafe(Mutable)Pointer<T>基礎數據。如果你需要的話,你總是可以通過向它添加/減去Int s來移動指針(例如獲得下一個值)。

編輯回答您的評論:您使用&來通過inout變量,然後可以在該函數中修改該變量。由於inout變量與傳遞指針基本相同,因此Swift開發人員決定通過&value來獲得期望UnsafeMutablePointer的參數。演示:

func inoutArray(inout array: [Int]) {} 

func pointerArray(array: UnsafeMutablePointer<Int>) {} 

var array = [1, 2, 3] 

inoutArray(&array) 
pointerArray(&array) 

這也適用於structs(也許一些其他的東西)

+0

我可能要看一看這一點。但它並沒有真正幫助我理解爲什麼其他結構(排列或不排列)似乎也能正常工作。 –

+0

@TravisGriggs更新了我的答案,看看 – Kametrixom

+0

不錯。我想,我快到了。仍然不清楚爲什麼一個[UInt8]可以直接放入UnsafeMutablePointer的地方?是否是inout運算符(&)的本質?我注意到我可以用上面的技巧來構建一個'NSData'。例如。 'NSData(&myStruct,sizeof(myStruct))',它工作正常。我的天真觀察是,&beforeVariable與C中的相似 –