2014-07-01 3 views
33

Sto cercando di memorizzare una serie di numeri interi su disco in modo rapido. Posso inserirli in un oggetto NSData da archiviare, ma è difficile rimetterli in una matrice. Posso ottenere un COpaquePointer grezzo con i dati con data.bytes ma non riesco a trovare un modo per inizializzare un nuovo array rapido con quel puntatore. Qualcuno sa come farlo?Creare una matrice in Swift da un oggetto NSData

import Foundation 

var arr : UInt32[] = [32,4,123,4,5,2]; 

let data = NSData(bytes: arr, length: arr.count * sizeof(UInt32)) 

println(data) //data looks good in the inspector 

// now get it back into an array? 
+5

archivio/estrarlo, che è il modo più semplice e più comune per fare una cosa simile. – holex

+1

qui si verifica un arresto anomalo dell'archivio/dell'archiviazione perché non è una matrice di oggetti, ma solo valori int. Dovrei cambiare il mio array in oggetti Int. – tassinari

+0

sicuro, perché è possibile archiviare solo oggetti conformi al protocollo 'NSCoding'. se si modifica la matrice da 'Array ' a 'Array ', sarà possibile archiviare immediatamente. – holex

risposta

57

È possibile utilizzare il metodo getBytes di NSData:

// the number of elements: 
let count = data.length/sizeof(UInt32) 

// create array of appropriate length: 
var array = [UInt32](count: count, repeatedValue: 0) 

// copy bytes into array 
data.getBytes(&array, length:count * sizeof(UInt32)) 

print(array) 
// Output: [32, 4, 123, 4, 5, 2] 

Aggiornamento per Swift 3 (Xcode 8): Swift 3 ha un nuovo tipo struct Data che è un wrapper per NS(Mutable)Data con semantica del valore appropriato. I metodi di accesso sono leggermente diversi.

Array di dati:

var arr: [UInt32] = [32, 4, UInt32.max] 
let data = Data(buffer: UnsafeBufferPointer(start: &arr, count: arr.count)) 
print(data) // <20000000 04000000 ffffffff> 

dati di Array:

let arr2 = data.withUnsafeBytes { 
    Array(UnsafeBufferPointer<UInt32>(start: $0, count: data.count/MemoryLayout<UInt32>.stride)) 
} 
print(arr2) // [32, 4, 4294967295] 
+0

Grazie, funziona benissimo. – tassinari

+1

Swift ha cambiato il modo di scrivere dichiarazioni di array, in Xcode Beta 5 viene visualizzato un errore che dice: 'I tipi di matrice sono ora scritti con le parentesi attorno al tipo di elemento'.Quindi nella seconda riga del codice il corretto è: 'var array = [UInt32] (count: count, repeatValue: 6)' – Lucien

+0

@Lucien: hai ragione, grazie, l'ho risolto. –

12

È anche possibile farlo utilizzando un UnsafeBufferPointer, che è essenzialmente un "puntatore", come implementa il protocollo Sequence:

let data = NSData(/* ... */) 

// Have to cast the pointer to the right size 
let pointer = UnsafePointer<UInt32>(data.bytes) 
let count = data.length/4 

// Get our buffer pointer and make an array out of it 
let buffer = UnsafeBufferPointer<UInt32>(start:pointer, count:count) 
let array = [UInt32](buffer) 

Ciò elimina la necessità di inizializzare un array vuoto con elementi duplicati per primo, per poi sovrascriverlo, anche se non ho idea se sia più veloce. Poiché utilizza il protocollo Sequence, ciò implica l'iterazione anziché la copia veloce della memoria, anche se non so se è ottimizzata quando viene passato un puntatore del buffer. Inoltre, non sono sicuro di quanto sia veloce l'inizializzatore "creare un array vuoto con X identici".

+0

Attualmente sembra funzionare meglio, poiché il metodo getBytes stava causando un arresto anomalo. – voidref

+5

sembra che getBytes sia molto più veloce rispetto a questo. Per 1349890 byte utilizzando un "puntatore di array" sono stati necessari 0,034 secondi, mentre getBytes solo 0,005 secondi – f3n1kc

+0

Come errore successivo: "UnsafePointer " con un elenco di argomenti di tipo "(UnsafeRawPointer)". perché data.bytes è UnsafeRawPointer. Sto solo cercando di scrivere un codice equivalente per const char * bytes = [data byte]; – Alix

0

Ecco un modo generico per farlo.

import Foundation 

extension Data { 
    func elements <T>() -> [T] { 
     return withUnsafeBytes { 
      Array(UnsafeBufferPointer<T>(start: $0, count: count/MemoryLayout<T>.size)) 
     } 
    } 
} 

let array = [1, 2, 3] 
let data = Data(buffer: UnsafeBufferPointer(start: array, count: array.count)) 
let array2: [Int] = data.elements() 

array == array2 
// IN THE PLAYGROUND, THIS SHOWS AS TRUE 

È necessario specificare il tipo nella linea array2. Altrimenti, il compilatore non può indovinare.

0

Se avete a che fare con i dati per array (che so per certo il mio allineamento sarà [String]), sono abbastanza contento di questo:

NSKeyedUnarchiver.unarchiveObject (con: yourdata)

spero che aiuta