objective arreglos array iphone objective-c cocoa struct nsarray

iphone - arreglos - ¿Cuál es la mejor manera de poner un c-struct en un NSArray?



nsarray swift (10)

En lugar de tratar de poner c struct en un NSArray, puede ponerlos en un NSData o NSMutableData como un conjunto de estructuras. Para acceder a ellos, lo harías

const struct MyStruct * theStruct = (const struct MyStruct*)[myData bytes]; int value = theStruct[2].integerNumber;

o para establecer entonces

struct MyStruct * theStruct = (struct MyStruct*)[myData mutableBytes]; theStruct[2].integerNumber = 10;

¿Cuál es la forma habitual de almacenar c-estructuras en un NSArray ? Ventajas, desventajas, manejo de memoria?

En particular, ¿cuál es la diferencia entre valueWithBytes y valueWithPointer ? valueWithPointer por justin y bagre a continuación.

Aquí hay un enlace a la discusión de Apple sobre valueWithBytes:objCType: para lectores futuros ...

Para un poco de pensamiento lateral y mirando más al rendimiento, Evgen ha planteado la cuestión del uso de STL::vector en C ++ .

(Esto plantea un problema interesante: ¿existe una biblioteca c rápida, no muy diferente de STL::vector pero mucho más liviana, que permite el "manejo ordenado de arreglos" mínimo ...?)

Entonces la pregunta original ...

Por ejemplo:

typedef struct _Megapoint { float w,x,y,z; } Megapoint;

Entonces, ¿cuál es la manera normal, mejor e idiomática de almacenar su propia estructura así en un NSArray , y cómo manejas la memoria en ese idioma?

Tenga en cuenta que estoy buscando específicamente el idioma habitual para almacenar estructuras. Por supuesto, uno podría evitar el problema haciendo una nueva clase pequeña. Sin embargo, quiero saber cómo es el modismo habitual para poner estructuras en una matriz, gracias.

Por cierto, aquí está el enfoque NSData, ¿cuál es tal vez? no es lo mejor ...

Megapoint p; NSArray *a = [NSArray arrayWithObjects: [NSData dataWithBytes:&p length:sizeof(Megapoint)], [NSData dataWithBytes:&p length:sizeof(Megapoint)], [NSData dataWithBytes:&p length:sizeof(Megapoint)], nil];

Por cierto, como punto de referencia y gracias a Jarret Hardie, esta es la forma de almacenar CGPoints y similares en un NSArray :

NSArray *points = [NSArray arrayWithObjects: [NSValue valueWithCGPoint:CGPointMake(6.9, 6.9)], [NSValue valueWithCGPoint:CGPointMake(6.9, 6.9)], nil];

(ver ¿Cómo puedo agregar objetos CGPoint a un NSArray de la manera más fácil? )


Le sugiero que se adhiera a la ruta NSValue , pero si realmente desea almacenar tipos de datos simples en su NSArray (y otros objetos de colección en Cocoa), puede hacerlo, aunque indirectamente, utilizando Core Foundation y toll- puenteo libre .

CFArrayRef (y su contraparte mutable, CFMutableArrayRef ) ofrecen al desarrollador más flexibilidad al crear un objeto de matriz. Ver el cuarto argumento del inicializador designado:

CFArrayRef CFArrayCreate ( CFAllocatorRef allocator, const void **values, CFIndex numValues, const CFArrayCallBacks *callBacks );

Esto le permite solicitar que el objeto CFArrayRef use las rutinas de administración de memoria de Core Foundation, ninguna o incluso sus propias rutinas de administración de memoria.

Ejemplo Obligatorio:

// One would pass &kCFTypeArrayCallBacks (in lieu of NULL) if using CF types. CFMutableArrayRef arrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL); NSMutableArray *array = (NSMutableArray *)arrayRef; struct {int member;} myStruct = {.member = 42}; // Casting to "id" to avoid compiler warning [array addObject:(id)&myStruct]; // Hurray! struct {int member;} *mySameStruct = [array objectAtIndex:0];

El ejemplo anterior ignora por completo los problemas con respecto a la gestión de la memoria. La estructura myStruct se crea en la pila y, por lo tanto, se destruye cuando la función finaliza: la matriz contendrá un puntero a un objeto que ya no está allí. Puede solucionar esto utilizando sus propias rutinas de administración de memoria, de ahí que se le proporcione la opción, pero luego tiene que hacer un trabajo arduo de recuento de referencias, asignación de memoria, desasignación, etc.

No recomendaría esta solución, pero la mantendré aquí en caso de que sea de interés para cualquier otra persona. :-)

Usando su estructura como asignado en el montón (en lugar de la pila) se demuestra aquí:

typedef struct { float w, x, y, z; } Megapoint; // One would pass &kCFTypeArrayCallBacks (in lieu of NULL) if using CF types. CFMutableArrayRef arrayRef = CFArrayCreateMutable(kCFAllocatorDefault, 0, NULL); NSMutableArray *array = (NSMutableArray *)arrayRef; Megapoint *myPoint = malloc(sizeof(Megapoint); myPoint->w = 42.0f; // set ivars as desired.. // Casting to "id" to avoid compiler warning [array addObject:(id)myPoint]; // Hurray! Megapoint *mySamePoint = [array objectAtIndex:0];


Le sugiero que use std :: vector o std :: list para tipos C / C ++, porque al principio es más rápido que NSArray, y en el segundo si no hay suficiente velocidad para usted, siempre puede crear su propio asignadores para contenedores STL y los hacen aún más rápidos. Todos los motores móviles modernos de juegos, física y audio usan contenedores STL para almacenar datos internos. Solo porque realmente rápido.

Si no es para ti, hay buenas respuestas de chicos sobre NSValue, creo que es lo más aceptable.


Puede usar clases NSObject que no sean C-Structures para almacenar información. Y puede almacenar fácilmente ese NSObject en NSArray.



Un método similar para agregar c struct es almacenar el puntero y eliminar la referencia del puntero como tal;

typedef struct BSTNode { int data; struct BSTNode *leftNode; struct BSTNode *rightNode; }BSTNode; BSTNode *rootNode; //declaring a NSMutableArray @property(nonatomic)NSMutableArray *queues; //storing the pointer in the array [self.queues addObject:[NSValue value:&rootNode withObjCType:@encode(BSTNode*)]]; //getting the value BSTNode *frontNode =[[self.queues objectAtIndex:0] pointerValue];


Un objeto Obj C es solo una estructura C con algunos elementos agregados. Así que solo crea una clase personalizada y tendrás el tipo de C struct que requiere un NSArray. Cualquier estructura de C que no tenga la fracción adicional que incluye un NSObject dentro de su estructura C será indigerible para una NSArray.

El uso de NSData como contenedor puede almacenar solo una copia de las estructuras y no las estructuras originales, si eso hace una diferencia para usted.


sería mejor utilizar el serializador objc de un pobre si estás compartiendo esta información en múltiples abis / arquitecturas:

Megapoint mpt = /* ... */; NSMutableDictionary * d = [NSMutableDictionary new]; assert(d); /* optional, for your runtime/deserialization sanity-checks */ [d setValue:@"Megapoint" forKey:@"Type-Identifier"]; [d setValue:[NSNumber numberWithFloat:mpt.w] forKey:@"w"]; [d setValue:[NSNumber numberWithFloat:mpt.x] forKey:@"x"]; [d setValue:[NSNumber numberWithFloat:mpt.y] forKey:@"y"]; [d setValue:[NSNumber numberWithFloat:mpt.z] forKey:@"z"]; NSArray *a = [NSArray arrayWithObject:d]; [d release], d = 0; /* ... */

... especialmente si la estructura puede cambiar con el tiempo (o por plataforma específica). no es tan rápido como otras opciones, pero es menos probable que se rompa en algunas condiciones (que no ha especificado como importantes o no).

si la representación serializada no sale del proceso, entonces el tamaño / orden / alineación de las estructuras arbitrarias no debería cambiar, y hay opciones que son más simples y más rápidas.

en cualquier caso, ya está agregando un objeto ref-counted (comparado con NSData, NSValue) así que ... crear una clase objc que contenga Megapoint es la respuesta correcta en muchos casos.


si te sientes nerd, o realmente tienes muchas clases para crear: ocasionalmente es útil construir dinámicamente una clase objc (ref: class_addIvar ). De esta forma, puedes crear clases de objc arbitrarias a partir de tipos arbitrarios. puede especificar campo por campo, o simplemente pasar la información de la estructura (pero eso es prácticamente la replicación de NSData). a veces útil, pero probablemente sea más un "hecho divertido" para la mayoría de los lectores.

¿Cómo aplicaría esto aquí?

puede llamar a class_addIvar y agregar una variable de instancia Megapoint a una nueva clase, o puede sintetizar una variante objc de la clase Megapoint en tiempo de ejecución (por ejemplo, una variable de instancia para cada campo de Megapoint).

el primero es equivalente a la clase objc compilada:

@interface MONMegapoint { Megapoint megapoint; } @end

el último es equivalente a la clase objc compilada:

@interface MONMegapoint { float w,x,y,z; } @end

después de agregar los ivars, puede agregar / sintetizar métodos.

para leer los valores almacenados en el extremo receptor, use sus métodos sintetizados, object_getInstanceVariable o valueForKey: (que a menudo convertirá estas variables escalares de instancia en representaciones NSNumber o NSValue).

Por cierto: todas las respuestas que has recibido son útiles, algunas son mejores / peores / inválidas según el contexto / escenario. las necesidades específicas de memoria, velocidad, facilidad de mantenimiento, facilidad para transferir o archivar, etc. determinarán cuál es la mejor para un caso dado ... pero no existe una solución "perfecta" que sea ideal en todos los aspectos. no hay "la mejor manera de poner una estructura en C en un NSArray", simplemente una "mejor manera de poner una estructura en C en un NSArray para un escenario, caso o conjunto de requisitos específicos ", que tendrías para especificar.

además, NSArray es una interfaz de matriz generalmente reutilizable para tipos de puntero (o más pequeños), pero hay otros contenedores que son más adecuados para c-structs por muchas razones (std :: vector es una opción típica para c-structs).


NSValue no solo es compatible con las estructuras de CoreGraphics; también puede usarlo usted mismo. Recomendaría hacerlo, ya que la clase probablemente sea más ligera que NSData para estructuras de datos simples.

Simplemente use una expresión como la siguiente:

[NSValue valueWithBytes:&p objCType:@encode(Megapoint)];

Y para recuperar el valor:

Megapoint p; [value getValue:&p];