Quelle est la manière habituelle de stocker des c-structures dans un NSArray
? Avantages, inconvénients, gestion de la mémoire?
Notamment, quelle est la différence entre valueWithBytes
et valueWithPointer
- élevé par Justin et le poisson-chat ci-dessous.
Voici un lien vers la discussion d'Apple sur les valueWithBytes:objCType:
futurs lecteurs ...
Pour une réflexion latérale et un examen plus approfondi des performances, Evgen a soulevé le problème de l'utilisation STL::vector
en C ++ .
(Cela soulève un problème intéressant: existe-t-il une bibliothèque c rapide, pas sans rappeler STL::vector
mais beaucoup plus légère, qui permet une "gestion ordonnée des tableaux" minimale ...?)
Donc la question initiale ...
Par exemple:
typedef struct _Megapoint {
float w,x,y,z;
} Megapoint;
Alors: quelle est la manière normale, la meilleure et idiomatique de stocker sa propre structure comme ça dans un NSArray
, et comment gérez-vous la mémoire dans cet idiome?
Veuillez noter que je recherche spécifiquement l'idiome habituel pour stocker des structures. Bien sûr, on pourrait éviter le problème en créant une nouvelle petite classe. Cependant, je veux savoir comment est l'idiome habituel pour mettre réellement des structures dans un tableau, merci.
BTW voici l'approche NSData 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 point de référence et grâce à Jarret Hardie, voici comment stocker CGPoints
et similaires dans un 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 facilement des objets CGPoint à un NSArray? )
Réponses:
NSValue ne prend pas uniquement en charge les structures CoreGraphics - vous pouvez également l'utiliser pour vous-même. Je recommanderais de le faire, car la classe est probablement plus légère que
NSData
pour les structures de données simples.Utilisez simplement une expression comme celle-ci:
Et pour récupérer la valeur:
la source
p
, pas un pointeur vers elle. La@encode
directive fournit toutes les informations nécessaires sur la taille de la structure. Lorsque vous relâchez leNSValue
(ou lorsque le tableau le fait), sa copie de la structure est détruite. Si vous avez utiliségetValue:
entre-temps, vous allez bien. Consultez la section «Utilisation des valeurs» des «Rubriques de programmation des nombres et des valeurs»: developer.apple.com/library/ios/documentation/Cocoa/Conceptual/…@encode
décrirait la structure avec ce pointeur, mais ne décrirait pas complètement les données pointées, ce qui pourrait en effet changer.NSValue
automatiquement la mémoire de la structure lorsqu'elle est désallouée? La documentation est un peu floue à ce sujet.NSValue
possède les données qu'il copie sur lui-même et je n'ai pas à me soucier de les libérer (sous ARC)?NSValue
ne fait pas vraiment de «gestion de la mémoire» en soi - vous pouvez le considérer comme une simple copie de la valeur de la structure en interne. Si la structure contenait des pointeurs imbriqués, par exemple,NSValue
ne saurait libérer, copier ou faire quoi que ce soit avec ceux-ci, cela les laisserait intacts, en copiant l'adresse telle quelle.Je vous suggère de vous en tenir à la
NSValue
route, mais si vous souhaitez vraiment stocker desstruct
types de données simples dans votre NSArray (et d'autres objets de collection dans Cocoa), vous pouvez le faire - bien qu'indirectement, en utilisant Core Foundation et un pont sans frais .CFArrayRef
(et son équivalent mutable,CFMutableArrayRef
) offrent au développeur plus de flexibilité lors de la création d'un objet tableau. Voir le quatrième argument de l'initialiseur désigné:Cela vous permet de demander que l'
CFArrayRef
objet utilise les routines de gestion de la mémoire de Core Foundation, aucune ou même vos propres routines de gestion de la mémoire.Exemple obligatoire:
L'exemple ci-dessus ignore complètement les problèmes liés à la gestion de la mémoire. La structure
myStruct
est créée sur la pile et est donc détruite lorsque la fonction se termine - le tableau contiendra un pointeur vers un objet qui n'est plus là. Vous pouvez contourner ce problème en utilisant vos propres routines de gestion de la mémoire - d'où la raison pour laquelle l'option vous est fournie - mais vous devez ensuite effectuer le dur travail de comptage de références, d'allocation de mémoire, de désallocation, etc.Je ne recommanderais pas cette solution, mais je la garderai ici au cas où elle intéresserait quelqu'un d'autre. :-)
L'utilisation de votre structure allouée sur le tas (au lieu de la pile) est illustrée ici:
la source
struct
, vous pourriez certainement l'attribuer une fois et ne pas le libérer à l'avenir. J'ai inclus un exemple de cela dans ma réponse modifiée. De plus, ce n'était pas une faute de frappemyStruct
, car il s'agissait d'une structure allouée sur la pile, distincte d'un pointeur vers une structure allouée sur le tas.Une méthode similaire pour ajouter une structure c est de stocker le pointeur et de dé-référencer le pointeur comme tel;
la source
si vous vous sentez ringard, ou avez vraiment beaucoup de classes à créer: il est parfois utile de construire dynamiquement une classe objc (ref:)
class_addIvar
. de cette façon, vous pouvez créer des classes objc arbitraires à partir de types arbitraires. vous pouvez spécifier champ par champ, ou simplement transmettre les informations de la structure (mais cela réplique pratiquement NSData). parfois utile, mais probablement plus un «fait amusant» pour la plupart des lecteurs.vous pouvez appeler class_addIvar et ajouter une variable d'instance Megapoint à une nouvelle classe, ou vous pouvez synthétiser une variante objc de la classe Megapoint au moment de l'exécution (par exemple, une variable d'instance pour chaque champ de Megapoint).
le premier est équivalent à la classe objc compilée:
ce dernier est équivalent à la classe objc compilée:
après avoir ajouté les ivars, vous pouvez ajouter / synthétiser des méthodes.
pour lire les valeurs stockées à l'extrémité de réception, utilisez vos méthodes synthétisées
object_getInstanceVariable
, ouvalueForKey:
(qui convertiront souvent ces variables d'instance scalaires en représentations NSNumber ou NSValue).btw: toutes les réponses que vous avez reçues sont utiles, certaines sont meilleures / pires / invalides selon le contexte / scénario. des besoins spécifiques en matière de mémoire, de vitesse, de facilité de maintenance, de facilité de transfert ou d'archivage, etc. détermineront ce qui est le mieux pour un cas donné ... mais il n'y a pas de solution «parfaite» qui soit idéale à tous égards. il n'y a pas de `` meilleur moyen de mettre un c-struct dans un NSArray '', juste un `` meilleur moyen de mettre un c-struct dans un NSArray pour un scénario, un cas ou un ensemble d'exigences spécifiques '' - ce que vous auriez spécifier.
De plus, NSArray est une interface de tableau généralement réutilisable pour les types de taille pointeur (ou plus petits), mais il existe d'autres conteneurs qui conviennent mieux aux c-structs pour de nombreuses raisons (std :: vector étant un choix typique pour les c-structs).
la source
std::vector
(par exemple) est plus adapté pour contenir des types, des structures et des classes C / C ++ que NSArray. En utilisant un NSArray de types NSValue, NSData ou NSDictionary, vous perdez beaucoup de sécurité de type tout en ajoutant une tonne d'allocations et de surcharge d'exécution. Si vous voulez vous en tenir à C, ils utiliseraient généralement malloc et / ou des tableaux sur la pile ... mais vousstd::vector
cache la plupart des complications.il serait préférable d'utiliser le sérialiseur objc du pauvre si vous partagez ces données entre plusieurs abis / architectures:
... en particulier si la structure peut évoluer dans le temps (ou par plateforme ciblée). ce n'est pas aussi rapide que d'autres options, mais il est moins susceptible de se briser dans certaines conditions (que vous n'avez pas spécifiées comme importantes ou non).
si la représentation sérialisée ne quitte pas le processus, alors la taille / l'ordre / l'alignement des structures arbitraires ne devraient pas changer, et il existe des options qui sont plus simples et plus rapides.
dans les deux cas, vous ajoutez déjà un objet ref-counted (par rapport à NSData, NSValue) donc ... créer une classe objc qui contient Megapoint est la bonne réponse dans de nombreux cas.
la source
Je vous suggère d'utiliser std :: vector ou std :: list pour les types C / C ++, car au début c'est juste plus rapide que NSArray, et dans un second temps s'il n'y aura pas assez de vitesse pour vous - vous pouvez toujours créer le vôtre allocateurs pour les conteneurs STL et les rendre encore plus rapides. Tous les moteurs mobiles modernes de jeu, de physique et audio utilisent des conteneurs STL pour stocker les données internes. Juste parce qu'ils sont vraiment rapides.
Si ce n'est pas pour vous - il y a de bonnes réponses de gars à propos de NSValue - je pense que c'est plus acceptable.
la source
Megapoint pt[8];
- c'est une option en c ++ et des conteneurs C ++ spécialisés (par exemplestd::array
) - notez également que l'exemple n'ajoute pas l'alignement spécial (16 octets ont été choisis car c'est la taille de Megapoint). (suite)std::vector
ajoutera une petite quantité de frais généraux à cela, et une allocation (si vous connaissez la taille dont vous aurez besoin) ... mais c'est plus proche du métal que plus de 99,9% des cas ont besoin. généralement, vous utiliseriez simplement un vecteur à moins que la taille ne soit fixe ou n'ait un maximum raisonnable.Au lieu d'essayer de mettre une structure c dans un NSArray, vous pouvez les placer dans un NSData ou NSMutableData en tant que tableau ac de structures. Pour y accéder, vous feriez
ou pour régler alors
la source
Bien que l'utilisation d'une NSValue fonctionne correctement pour stocker des structures en tant qu'objet Obj-C, vous ne pouvez pas encoder une NSValue contenant une structure avec NSArchiver / NSKeyedArchiver. Au lieu de cela, vous devez encoder des membres de structure individuels ...
Consultez le guide de programmation des archives et des sérialisations d'Apple> Structures et champs de bits
la source
Pour votre structure, vous pouvez ajouter un attribut
objc_boxable
et utiliser la@()
syntaxe pour mettre votre structure dans l'instance NSValue sans appelervalueWithBytes:objCType:
:la source
Un objet Obj C est juste une structure C avec quelques éléments ajoutés. Alors créez simplement une classe personnalisée et vous aurez le type de structure C requis par un NSArray. Toute structure C qui n'a pas la cruauté supplémentaire qu'un NSObject inclut dans sa structure C sera indigeste à un NSArray.
L'utilisation de NSData comme wrapper peut ne stocker qu'une copie des structures et non les structures d'origine, si cela fait une différence pour vous.
la source
Vous pouvez utiliser des classes NSObject autres que les C-Structures pour stocker des informations. Et vous pouvez facilement stocker ce NSObject dans NSArray.
la source