2015-08-31 8 views
5

Ich finde Swift und NSData eine unheilige Ehe der Frustration zu sein. Ich finde, ich fühle mich wie all die vermeintlichen Neugefundenen. Schnelle Sicherheit geht jedes Mal aus dem Fenster, wenn ich mit diesem Ding klarkomme. Die Anzahl der Abstürze (mit nicht hilfreichen Spuren) hilft nicht.Daten aus NSData mit Swift erhalten

Also, ich habe gelernt, dass ich Dinge zu tun, wie die nach den beängstigend UnsafeMutablePointer Sachen vermeiden:

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

ich, dass auch entdeckt, ich direkt in einzelne Werte extrahieren kann:

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

Dies führt zu zwei Zwischen Fragen:

1) gibt es etwas, kann ich, dass zuverlässiger ist es als hartkodierte Konstanten. Wenn dies C wäre, würde ich einfach sizeof verwenden. Aber ich denke, dass ich gelesen habe, dass ich vielleicht statt sizeof verwenden sollte? Und das würde nicht auf [UInt8] funktionieren, oder?

2) Die Dokumentation (für Swift) besagt, dass dieser Parameter _ buffer: UnsafeMutablePointer<Void> sein soll. Wie funktioniert das? Bekomme ich nur Glück? Warum sollte ich das anstelle des nativer/verwalteten [Uint8] Konstrukts tun? Ich fragte mich, ob UnsafeMutablePointer ein Protokoll war, aber es ist eine Struktur.

Ermutigt mit dem Lesen der Werte direkt (anstatt als ein Array), dachte ich, vielleicht könnte ich eine andere Art von Struktur versuchen. Ich habe eine 6-Byte-Struktur, die wie folgt aussieht:

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

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

Welche tatsächlich funktioniert (nach dem Denken sie nicht, aber schließlich ein sauberes tun, die einige Abstürze gehen weg gemacht)!

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

Aber das führt mich zu Sorgen über Struktur Pack Details? Warum funktioniert das? Worüber sollte ich mir Sorgen machen?

Das alles fühlt sich sehr C-ish für mich an. Ich dachte Swift nahm das C aus ObjectiveC heraus.

Antwort

2

Vielleicht möchten Sie überprüfen, RawData, die wirklich neu ist und dieser Typ nur ein wenig mit dieser Idee experimentiert, also glaube nicht, dass es gut getestet oder irgendetwas, einige Funktionen sind noch nicht einmal implementiert. Es ist im Grunde ein Swift-y-Wrapper um (Sie haben es erraten) Rohdaten, eine Reihe von Bytes.

diese Erweiterung verwenden, können Sie es initialisieren mit einem NSData Beispiel:

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

you'de es wie folgt anrufen:

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

let rawData = RawData(data: data) 

EDIT: Ihre Fragen zu beantworten:

Die Sache ist, dass Daten groß, sehr groß sein können. Im Allgemeinen möchten Sie keine großen Dateien kopieren, da der Speicherplatz wertvoll ist. Der Unterschied zwischen einem Array von [UInt8] Werten und einer NSData Instanz ist, dass das Array jedes Mal kopiert wird, Sie geben es einer Funktion -> neue Kopie, Sie tun eine Zuweisung -> neue Kopie. Das ist bei großen Daten nicht sehr wünschenswert.

1) Wenn Sie die meisten einheimischen, sichere Art und Weise wollen, ohne 3rd-Party-Bibliotheken wie die genannten, können Sie dies tun:

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

(Ich weiß es nicht sehr sicher klingt, aber glaube mir, es ist). Sie können dies fast wie ein gewöhnliches Array verwenden:

und es kopiert nicht die Daten, wie Sie es weitergeben.

2) UnsafeMutablePointer<Void> ist in der Tat sehr C-like, in diesem Zusammenhang stellt es den Startwert (auch Basis genannt) in einer Folge von Zeigern. Der Typ Void kommt auch von C, es bedeutet, dass der Zeiger nicht weiß, welche Art von Wert er speichert. Sie können alle Arten von Zeigern auf den Typ, den Sie erwarten, wie folgt darstellen: UnsafeMutablePointer<Int>(yourVoidPointer) (Dies sollte nicht abstürzen). Wie bereits erwähnt, können Sie UnsafeMutableBufferPointer verwenden, um es als eine Sammlung Ihres Typs zu verwenden. UnsafeMutableBufferPointer ist nur ein Wrapper um Ihren Basiszeiger und die Länge (dies erklärt den Initializer, den ich verwendet habe).

Ihre Methode, die Daten direkt in Ihre Struktur zu dekodieren, funktioniert tatsächlich, die Eigenschaften einer Struktur sind in der richtigen Reihenfolge, selbst nach der Kompilierzeit, und die Größe einer Struktur ist genau die Summe ihrer gespeicherten Eigenschaften. Für einfache Daten wie deine ist das völlig in Ordnung. Es gibt eine Alternative: Um das NSCoding Protokoll zu verwenden. Vorteil: sicherer. Nachteil: Sie müssen NSObject ableiten. Ich denke, du solltest an der Art und Weise festhalten, wie du es jetzt machst. Eine Sache, die ich ändern würde, ist, die Entschlüsselung Ihrer Struktur innerhalb der Struktur selbst zu setzen und sizeof zu verwenden. Haben Sie es wie folgt aus:

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

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

Ein weiterer EDIT: Sie können jederzeit die zugrunde liegenden Daten aus einem Unsafe(Mutable)Pointer<T> mit dem Verfahren erhalten memory deren Rückgabetyp ist T. Wenn Sie müssen, können Sie Zeiger immer verschieben (um den nächsten Wert zu erhalten), indem Sie einfach Int s hinzufügen/subtrahieren.

EDIT Beantworten Sie Ihren Kommentar: Sie verwenden die &, um eine inout Variable zu übergeben, die dann innerhalb der Funktion geändert werden kann. Da eine inout Variable im Grunde die gleiche ist wie die Übergabe des Zeigers, haben die Swift Devs beschlossen, &value für ein Argument zu übergeben, das eine UnsafeMutablePointer erwartet. Demonstration:

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

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

var array = [1, 2, 3] 

inoutArray(&array) 
pointerArray(&array) 

Dies funktioniert auch für structs (und vielleicht einige andere Dinge)

+0

Ich muss einen Blick auf das in Anspruch nehmen. Aber es hilft mir nicht wirklich zu verstehen, warum andere Strukturen (geordnet oder nicht) auch gut zu funktionieren scheinen. –

+0

@TravisGriggs Aktualisiert meine Antwort, überprüfen Sie es – Kametrixom

+0

Nizza. Ich bin fast da, denke ich. Noch ist unklar, warum ein [UInt8] direkt dort eingefügt werden kann, wo ein UnsafeMutablePointer geht? Ist es die Art des Inout-Operators (&)? Ich habe bemerkt, dass ich das Gegenteil der obigen Technik verwenden kann, um ein 'NSData' zu konstruieren. Z.B. 'NSData (& myStruct, sizeof (myStruct))' und es funktioniert gut. Meine naive Beobachtung ist, dass die & vorVariable ähnlich ist wie in C. –