web-dev-qa-db-fra.com

Créer un tableau dans Swift à partir d'un objet NSData

J'essaie de stocker un tableau d'entiers sur le disque dans Swift. Je peux les mettre dans un objet NSData à stocker, mais les récupérer dans un tableau est difficile. Je peux obtenir un COpaquePointer brut aux données avec data.bytes mais ne trouve pas de moyen d'initialiser un nouveau tableau Swift avec ce pointeur. Quelqu'un sait-il comment le faire?

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?
34
tassinari

Vous pouvez utiliser la méthode getBytes de 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]

Mise à jour pour Swift 3 (Xcode 8): Swift 3 a un nouveau type struct Data Qui est un wrapper pour NS(Mutable)Data avec une sémantique de valeur appropriée. Les méthodes d'accesseur sont légèrement différentes.

Tableau à données:

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

Données à mettre en réseau:

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

Mise à jour pour Swift 5:

Tableau à données:

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

Données à mettre en réseau:

var arr2 = Array<UInt32>(repeating: 0, count: data.count/MemoryLayout<UInt32>.stride)
_ = arr2.withUnsafeMutableBytes { data.copyBytes(to: $0) }
print(arr2) // [32, 4, 4294967295]
64
Martin R

Il est également possible de le faire en utilisant un UnsafeBufferPointer, qui est essentiellement un "pointeur de tableau", car il implémente le protocole 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)

Cela élimine le besoin d'initialiser d'abord un tableau vide avec des éléments dupliqués, puis de l'écraser, bien que je ne sache pas s'il est plus rapide. Comme il utilise le protocole Sequence, cela implique une itération plutôt qu'une copie rapide de la mémoire, bien que je ne sais pas s'il est optimisé lorsqu'il est passé un pointeur de tampon. Là encore, je ne suis pas sûr de la vitesse de l'initialiseur "créer un tableau vide avec X éléments identiques".

14
Daniel Bruce

Si vous avez affaire à Data to Array (je sais avec certitude que mon tableau sera [String]), je suis assez satisfait de ceci:

NSKeyedUnarchiver.unarchiveObject (avec: vos données)

J'espère que ça aide

1
Michel Goñi

Voici une façon générique de le faire.

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

Vous devez spécifier le type dans le array2 ligne. Sinon, le compilateur ne peut pas deviner.

0
William Entriken