89 votes

Ce qui ' s la meilleure façon de mettre un c-struct dans un NSArray ?

Quelle est la façon habituelle de magasin, c-structures en NSArray? Les avantages, les inconvénients, la gestion de la mémoire?

Notamment, quelle est la différence entre valueWithBytes et valueWithPointer -- soulevées par justin et le poisson-chat ci-dessous.

Voici un lien pour Apple discussion de l' valueWithBytes:objCType: pour les futurs lecteurs...

Pour certains, la pensée latérale et la recherche de plus de performance, Evgen'a soulevé la question de l'utilisation de STL::vector en C++.

(Cela soulève une question intéressante: est-il rapide de la bibliothèque c, pas à la différence de STL::vector , mais beaucoup plus léger, qui permet pour les minimes "en ordre de la manipulation de tableaux" ...?)

Si la question d'origine...

Par exemple:

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

Donc: quel est le normal, le meilleur, idiomatiques façon de stocker une structure comme ça dans un NSArray, et comment avez-vous gérer la mémoire dans cet idiome?

Veuillez noter que je recherche précisément l'habitude idiome pour stocker des structures. Bien sûr, on pourrait éviter le problème en faisant une nouvelle petite classe. Cependant, je voudrais savoir comment l'habitude d'un langage pour l'application de structs dans un tableau, merci.

BTW voici la NSData approche qui est peut-être? pas mieux...

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];

BTW comme un point de référence et grâce à Jarret Hardie, voici comment stocker CGPoints et similaires, en NSArray:

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

(voir Comment puis-je ajouter CGPoint objets à un NSArray la voie de la facilité?)

163voto

Justin Spahr-Summers Points 12167

NSValue n'est pas seulement un soutien CoreGraphics structures – vous pouvez l'utiliser pour votre propre trop. Je recommande de le faire, car la classe est probablement un poids plus léger que l' NSData pour de simples structures de données.

Simplement utiliser une expression comme suit:

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

Et pour obtenir la valeur de retour:

Megapoint p;
[value getValue:&p];

7voto

Sedate Alien Points 5022

Je vous suggère de coller à l' NSValue route, mais si vraiment vous ne souhaitez stocker la plaine de 'ol struct types de données dans votre NSArray (et d'autres objets de collection dans le Cacao), vous pouvez le faire, quoique indirectement, à l'aide de Fondement et sans frais de transition.

CFArrayRef (et de ses mutable contrepartie, CFMutableArrayRef) permettre au développeur de plus de flexibilité lors de la création d'un objet de tableau. Voir la quatrième argument de l'désignés initialiser:

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

Cela vous permet de demander que l' CFArrayRef l'utilisation des objets de Base de la Fondation des routines de gestion de mémoire, pas du tout ou même vos propres routines de gestion de mémoire.

Obligatoire exemple:

// 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];

L'exemple ci-dessus ignore complètement les questions relatives à la gestion de la mémoire. La structure de l' myStruct est créé sur la pile, et donc est détruite lorsque la fonction se termine -- le tableau contient un pointeur vers un objet qui n'est plus là. Vous pouvez contourner ce problème en utilisant vos propres routines de gestion de mémoire -- c'est pourquoi l'option est fournie pour vous, mais vous avez à faire le travail difficile de comptage de référence, l'allocation de mémoire, libérer et ainsi de suite.

Je ne recommande pas cette solution, mais le garder ici dans le cas où il est de l'intérêt à quelqu'un d'autre. :-)


À l'aide de votre structure alloué sur le tas (en remplacement de la pile) est démontré ici:

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];

3voto

justin Points 72871

il serait préférable d'utiliser les pauvres de l'homme-objc sérialiseur si vous êtes le partage de ces données à travers de multiples abis/architectures:

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;
/* ... */

...en particulier si la structure peut changer au fil du temps (ou par la plateforme ciblée). ce n'est pas aussi rapide que les autres options, mais il est moins susceptibles de se briser dans certaines conditions (que vous n'avez pas spécifié comme important ou non).

si la représentation sérialisée ne pas quitter le processus, puis la taille/l'ordre/l'alignement de l'arbitraire des structures ne doit pas changer, et il y a des options qui sont plus simples et plus rapides.

dans les deux cas, vous êtes déjà à l'ajout d'un ref-compté objet (par rapport à NSData, NSValue) donc... la création d'un objc classe qui détient Megapoint est la bonne réponse, dans de nombreux cas.

3voto

justin Points 72871

si vous vous sentez ringard, ou vraiment beaucoup de classes à créer: il est parfois utile de construire dynamiquement une objc classe (ref: class_addIvar). de cette façon, vous pouvez créer arbitraire objc classes de types arbitraires. vous pouvez spécifier un champ à l'autre, ou tout simplement passer l'info de la structure (mais c'est pratiquement la réplication NSData). parfois utile, mais probablement plus d'un "fait divers" pour la plupart des lecteurs.

Comment dois-je m'inscrire ici?

vous pouvez appeler class_addIvar et ajouter un Megapoint variable d'instance à une nouvelle classe, ou vous pouvez synthétiser un objc variante de la Megapoint classe au moment de l'exécution (par exemple, une variable d'instance pour chaque champ de Megapoint).

le premier est l'équivalent de la compilation des objc classe:

@interface MONMegapoint { Megapoint megapoint; } @end

ce dernier est l'équivalent de la compilés objc classe:

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

après que vous avez ajouté le ivars, vous pouvez ajouter/synthétiser des méthodes.

pour lire les valeurs enregistrées sur l'extrémité de réception, utilisez votre synthétisé méthodes, object_getInstanceVariableou valueForKey:(ce qui va souvent de convertir ces scalaire variables d'instance dans NSNumber ou NSValue représentations).

btw: toutes les réponses que vous avez reçues sont utiles, d'autres sont mieux/pire/invalide selon le contexte/scénario. besoins spécifiques concernant la mémoire, la rapidité, la facilité à maintenir, la facilité de transfert ou d'archives, etc. permettra de déterminer qui est le mieux pour un cas donné... mais il n'y a pas de solution "parfaite" qui est idéal dans tous les sens. il n'existe pas de meilleur moyen de mettre une c-structure dans un NSArray", juste une 'meilleure façon de mettre une c-structure dans un NSArray pour un scénario spécifique, cas, ou un ensemble d'exigences, et vous auriez à préciser.

en outre, NSArray est généralement réutilisables tableau de l'interface de pointeur de taille moyenne (ou plus petit), mais il y a d'autres conteneurs qui sont mieux adaptés pour c-les structures pour de nombreuses raisons (std::vector être un choix typique pour c-les structures).

0voto

Evgen Bodunov Points 1613

Je vous suggère d'utiliser std::vector ou std::list pour le C/C++ types, car au début, c'est juste plus rapide que NSArray, et au deuxième, si il n'y aura pas assez de vitesse pour vous - vous avez toujours la possibilité de créer votre propre allocateurs pour les conteneurs STL et les rendre encore plus rapide. Tous les mobile Jeu, la Physique et les moteurs Audio utilise des conteneurs STL pour stocker des données internes. Juste parce qu'ils ont vraiment rapide.

Si ce n'est pas pour vous - il ya de bonnes réponses de mecs sur NSValue - je pense que c'est plus acceptable.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X