swift struct nsdata swift2

Obteniendo datos de NSData con Swift



struct swift2 (1)

Estoy descubriendo que Swift y NSData son un matrimonio impío de frustración. Encuentro que siento que todo lo que se supone que es nuevo y encontrado, la seguridad de Swift sale por la ventana cada vez que trato con esta cosa. La cantidad de bloqueos (con rastros inútiles) no ayuda.

Por lo tanto, he aprendido que puedo evitar las cosas temibles de UnsafeMutablePointer haciendo cosas como las siguientes:

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

También descubrí que puedo extraer directamente en valores singulares:

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

Esto lleva a dos preguntas intermedias:

1) ¿Hay algo que pueda usar que sea más confiable que las constantes codificadas allí? Si esto fuera C, solo usaría sizeof . Pero creo que leí que tal vez debería usar strideof vez de sizeof . Y eso no funcionaría en [UInt8] , ¿verdad?

2) The docs (para Swift) dice que se supone que este parámetro es _ buffer: UnsafeMutablePointer<Void> . Entonces, ¿cómo funciona esto? ¿Estoy teniendo suerte? ¿Por qué querría hacer eso en lugar de la construcción más nativa / administrada [Uint8]? Me pregunté si UnsafeMutablePointer era un protocolo, pero es una estructura.

Envalentonado con la lectura de los valores directamente (en lugar de como una matriz), pensé que tal vez podría probar otro tipo de estructura. Tengo una estructura de 6 bytes que se ve así:

struct TreeDescription : Hashable { var id:UInt32 = 0x00000000 var channel:UInt8 = 0x00 var rssi:UInt8 = 0x00 var hashValue:Int { return Int(self.id) } }

Lo cual realmente funciona (¡después de pensar que no fue así, pero eventualmente hizo una limpieza que hizo que algunos accidentes desaparecieran)!

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

¿Pero esto me lleva a preocuparme por los detalles del embalaje de la estructura? ¿Por qué funciona esto? ¿De qué debería preocuparme por hacer esto?

Todo esto se siente muy C-ish para mí. Pensé que Swift sacó la C de ObjectiveC.


Es posible que desee comprobar RawData, que es realmente nuevo y este tipo acaba de experimentar un poco con esta idea, por lo que no creo que haya sido probado bien o algo así, alguna función aún no está implementada. Básicamente es una envoltura de Swift-y alrededor (lo adivinaste) datos en bruto, una serie de bytes.

Usando esta extensión, puede inicializarla con una instancia de NSData :

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

Lo estarás llamando así:

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

EDITAR: Para responder a sus preguntas:

El caso es que los datos pueden ser grandes, muy grandes. Por lo general, no desea copiar cosas grandes, ya que el espacio es valioso. La diferencia entre una matriz de valores [UInt8] y una instancia de NSData es que la matriz se copia cada vez, se le NSData una función -> nueva copia, se realiza una tarea -> nueva copia. Eso no es muy deseable con grandes datos.

1) Si desea la forma más nativa y segura, sin bibliotecas de terceros como la mencionada, puede hacer esto:

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

(Sé que no parece muy seguro, pero créanme que sí). Puede usar esto casi como una matriz común:

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

y no copia los datos a medida que los transmite.

2) UnsafeMutablePointer<Void> es muy similar a C, en este contexto representa el valor inicial (también llamado base) en una secuencia de punteros. El tipo Void viene de C, significa que el puntero no sabe qué tipo de valor está almacenando. Puede convertir todo tipo de punteros al tipo que espera de esta manera: UnsafeMutablePointer<Int>(yourVoidPointer) (Esto no debería bloquearse). Como se mencionó anteriormente, puede usar UnsafeMutableBufferPointer para usarlo como una colección de su tipo. UnsafeMutableBufferPointer es solo una envoltura alrededor de su puntero base y la longitud (esto explica el inicializador que utilicé).

Su método de decodificación de los datos directamente en su estructura sí funciona, las propiedades de una estructura están en el orden correcto, incluso después del tiempo de compilación, y el tamaño de una estructura es exactamente la suma de sus propiedades almacenadas. Para datos simples como el tuyo, eso está totalmente bien. Hay una alternativa: usar el protocolo NSCoding . Ventaja: más seguro. Desventaja: debe subclasificar NSObject. Creo que deberías mantenerte en la forma en que lo haces ahora. Sin embargo, una cosa que cambiaría es poner la decodificación de tu estructura dentro de la propia estructura y usar sizeof . Tenlo así:

struct TreeDescription { var id:UInt32 = 0x00000000 var channel:UInt8 = 0x00 var rssi:UInt8 = 0x00 init(data: NSData) { data.getBytes(&self, length: sizeof(TreeDescription)) } }

Otra EDIT: siempre puede obtener los datos subyacentes de un Unsafe(Mutable)Pointer<T> con la memory del método cuyo tipo de devolución es T Si lo necesita, siempre puede cambiar los punteros (para obtener el siguiente valor, por ejemplo) simplemente agregando / restando Int s a él.

EDITAR respondiendo tu comentario: usas & para pasar una variable inout , que luego se puede modificar dentro de la función. Como una variable inout es básicamente lo mismo que pasar el puntero, los desarrolladores Swift decidieron hacer posible pasar &value un argumento que espera un UnsafeMutablePointer . Demostración:

func inoutArray(inout array: [Int]) {} func pointerArray(array: UnsafeMutablePointer<Int>) {} var array = [1, 2, 3] inoutArray(&array) pointerArray(&array)

Esto también funciona para structs (y tal vez algunas otras cosas)