2015-08-31 37 views
5

Uważam, że Swift i NSData są bezbożnymi małżeństwami frustracji. Wydaje mi się, że czuję się tak, jak wszystkie nowe, znalezione bezpieczeństwo Swift wychodzi za każdym razem, gdy mam do czynienia z tym problemem. Ilość awarii (z nieprzydatnymi śladami) nie pomaga.Pobieranie danych z NSData za pomocą Swift

Więc nauczyłem się, że mogę uniknąć strasznego UnsafeMutablePointer rzeczy robiąc rzeczy, jak następuje:

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

odkryłem także, że można wyodrębnić bezpośrednio do wartości pojedynczej:

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

To prowadzi do dwóch pytań pośrednich:

1) Czy jest coś, co mogę użyć, to jest bardziej wiarygodne niż stałe kody tam. Gdyby to był C, po prostu użyłbym sizeof. Ale myślę, że przeczytałem, że może powinienem używać strideof zamiast sizeof? A to nie zadziałałoby na [UInt8], prawda?

2) Dokumenty (dla Swift) mówią, że ten parametr ma być _ buffer: UnsafeMutablePointer<Void>. Jak to działa? Czy po prostu mam szczęście? Dlaczego miałbym to robić zamiast bardziej natywnego/zarządzanego [Uint8] konstruktu? Zastanawiałem się, czy UnsafeMutablePointer był protokołem, ale jest to struktura.

Ośmielony odczytaniem wartości bezpośrednio (a nie jako Tablica), pomyślałem, że mógłbym spróbować innego rodzaju struktury. Mam struct 6 bajtów, który wygląda tak:

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

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

które faktycznie działa (po myśląc, że nie, ale w końcu robi to czysty co czyniło pewne awarie odejść)!

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

Ale to prowadzi mnie do obaw o strukturę szczegółów opakowania? Dlaczego to działa? O co powinienem się martwić?

To wszystko wydaje mi się bardzo C-owskie. Myślałem, że Swift zabrał C z ObjectiveC.

Odpowiedz

2

Być może zechcesz wypróbować RawData, który jest naprawdę nowy i ten facet po prostu trochę eksperymentował z tym pomysłem, więc nie myśl, że został on dobrze przetestowany lub cokolwiek, niektóre funkcje nie zostały jeszcze zaimplementowane. Jest to w zasadzie opakowanie Swift-y (zgadłeś), surowe dane, seria bajtów.

Korzystanie z tego rozszerzenia, można zainicjować go z instancją NSData:

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

You'de być nazywając ją tak:

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

let rawData = RawData(data: data) 

EDIT: Aby odpowiedzieć na Twoje pytania:

Chodzi o to, że dane mogą być duże, bardzo duże. Zwykle nie chcesz kopiować dużych rzeczy, ponieważ przestrzeń jest cenna. Różnica między tablicą wartości [UInt8] a instancją NSData polega na tym, że tablica jest kopiowana za każdym razem, daje się jej funkcję -> nowa kopia, wykonuje się zadanie -> nową kopię. To nie jest zbyt pożądane w przypadku dużych danych.

1) Jeśli chcesz najbardziej rodzimy, bezpieczny sposób, bez żadnych zewnętrznych bibliotek 3rd jak te wymienione, można to zrobić:

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

(wiem, że to nie brzmi bardzo bezpieczne, ale uwierz mi, że tak jest). Możesz użyć tego prawie tak, jak zwykłej tablicy:

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() 

i nie kopiuje danych podczas przekazywania.

2) UnsafeMutablePointer<Void> jest rzeczywiście bardzo podobne do C, w tym kontekście reprezentuje wartość początkową (zwaną także podstawową) w sekwencji wskaźników. Typ Void również pochodzi z języka C, co oznacza, że ​​wskaźnik nie wie, jaką wartość przechowuje. Możesz rzucić wszystkie rodzaje wskaźników do typu, którego oczekujesz w ten sposób: UnsafeMutablePointer<Int>(yourVoidPointer) (To nie powinno się zawieszać). Jak wspomniano wcześniej, możesz użyć UnsafeMutableBufferPointer, aby użyć go jako kolekcji swojego typu. UnsafeMutableBufferPointer to tylko otok wokół wskaźnika bazowego i jego długość (wyjaśnia to użyty inicjator).

Twoja metoda dekodowania danych bezpośrednio do Twojej struktury rzeczywiście działa, właściwości struktury są we właściwej kolejności, nawet po czasie kompilacji, a rozmiar struktury jest dokładnie sumą jej przechowywanych właściwości. Dla prostych danych, takich jak twoje, jest to całkowicie w porządku. Istnieje alternatywa: Aby użyć protokołu NSCoding. Zaleta: bezpieczniejsza. Wada: Musisz podklasować NSObject. Myślę, że powinieneś trzymać się tego, jak teraz to robisz. Jedną z rzeczy, którą chciałbym zmienić, jest umieszczenie dekodowania struktury wewnątrz samej struktury i użycie sizeof. Mieć to tak:

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

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

Kolejny EDIT: Zawsze można dostać podstawowymi danymi z Unsafe(Mutable)Pointer<T> metodą memory którego powrót typ jest T. Jeśli zajdzie taka potrzeba, zawsze możesz zmienić wskaźniki (aby uzyskać następną wartość np.), Dodając/odejmując do niej s. Int.

EDYCJA odpowiedzi na Twój komentarz: Używasz &, aby przekazać zmienną inout, którą następnie można zmodyfikować w ramach funkcji. Ponieważ zmienna inout jest zasadniczo taka sama jak podanie wskaźnika, twórcy Swift postanowili umożliwić przekazanie &value dla argumentu, który spodziewa się UnsafeMutablePointer. Demonstracja:

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

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

var array = [1, 2, 3] 

inoutArray(&array) 
pointerArray(&array) 

Działa to również dla structs (a może kilka innych rzeczy)

+0

może muszę spojrzeć na to. Ale to naprawdę nie pomaga mi zrozumieć, dlaczego inne konstrukcje (w szyku lub nie) również działają dobrze. –

+0

@TravisGriggs Zaktualizowano moją odpowiedź, sprawdź to. – Kametrixom

+0

Miło. Myślę, że prawie już tam jestem. Nadal nie wiadomo, dlaczego [UInt8] może zostać umieszczony bezpośrednio w miejscu, w którym porusza się UnsafeMutablePointer? Czy to jest charakter operatora inout (&)? Zauważyłem, że mogę użyć odwrotności powyższej techniki do skonstruowania 'NSData'. Na przykład. 'NSData (& myStruct, sizeof (myStruct))' i działa dobrze. Moja naiwna obserwacja jest taka, że ​​zmienna & beforeVariable jest podobna do tej w C. –