Objective-C ARC et passant C arrays d'objects

Je suis désolé si c'est un peu une question de C-Noob: je sais que j'ai besoin de swot sur mes pointeurs. Malheureusement, je n'ai pas le time de travailler sur un chapitre entier du livre, donc j'espère avoir un conseil un peu plus ciblé.

Je veux stocker des objects Objective-C dans un tableau C. J'utilise ARC. Si j'étais sur Mac, je serais en mesure d'utiliser NSPointerArray à la place, mais je suis sur iOS et ce n'est pas disponible.

Je vais stocker un tableau C sortingdimensionnel: conceptuellement, mes dimensions sont day, height et cacheNumber. Chaque élément sera soit un pointeur vers un object objective-C, soit NULL.

Le nombre de caches (c'est-à-dire la taille de la dimension cacheNumber) est connu au moment de la compilation, mais les deux autres ne sont pas connus. En outre, le tableau peut être très volumineux, je dois donc allouer dynamicment de la memory pour cela.

En ce qui concerne la sémantique de propriété, j'ai besoin de references fortes aux objects.

Je voudrais que l'set du tableau sortingdimensionnel soit une variable d'instance sur un object objective-C.

Je prévois d'avoir une méthode qui est - tableForCacheNumber:(int)num days:(int*)days height:(int*)height . Cette méthode doit renvoyer un tableau à deux dimensions, c'est-à-dire un numéro de cache spécifique. (Il repasse également par reference la taille du tableau qu'il returnne.)

Mes questions:

  • Dans quel ordre dois-je mettre mes dimensions pour que je puisse facilement returnner un pointeur vers le sous-réseau pour un numéro de cache spécifique? (Je pense que ça devrait être le premier, mais je ne suis pas à 100%.)

  • Quel devrait être le type de return de ma méthode, de sorte qu'ARC ne se plaint pas? Cela ne me dérange pas si le tableau returnné a un nombre de references augmenté ou non, tant que je sais ce qu'il fait.

  • Quel type doit être ma variable d'instance qui contient le tableau sortingdimensionnel? Je pense que cela devrait juste être un pointeur, puisque cet ivar représente juste le pointeur vers le premier élément qui est dans mon tableau. Correct? Si oui, comment puis-je le spécifier?

  • Quand je crée le tableau sortingdimensionnel (pour mon ivar), je suppose que je fais quelque chose comme calloc(X * Y * Z, sizeof(id)) , et jette le résultat au type pour mon ivar?

  • Lorsque j'accède à des éléments du tableau sortingdimensionnel dans l'ivar, je crois que je dois déreferencer le pointeur à chaque fois, avec quelque chose comme (*myArray)[4][7][2] . Correct?

  • Le tableau bidimensionnel que je reviens de la méthode sera-t-il accessible de la même manière?

  • Dois-je marquer le tableau bidimensionnel objc_returns_inner_pointer avec objc_returns_inner_pointer ?

Je suis désolé encore une fois que ce soit un peu une mauvaise question Stack Overflow (c'est trop long et avec trop de parties). J'espère que les citoyens de SO me pardonneront. Pour améliorer mon interweb karma, peut-être que je l'écrirai comme un billet de blog lorsque ce projet aura été livré.

Tout d'abord: bien que vous n'ayez pas NSPointerArray , vous avez CFMutableArrayRef et vous pouvez passer tous les callbacks que vous voulez conserver / release / description, y compris NULL . C'est peut-être plus facile (et la performance est quelque chose que vous pouvez mesurer plus tard) d'essayer cela en premier.

Prendre vos points dans l'ordre:

  • vous devriez définir vos dimensions comme [cacheNumber][days][height] , comme vous le souhaitez. Puis le cache[cacheNumber] est un tableau à deux dimensions de type id *[][] . Comme vous l'avez dit, la performance est importante, sachez que le moyen le plus rapide d'itérater cette bête est:

     for (/* cacheNumber loop */) { for (/* days loop */) { for (/* height loop */) { //... } } } 
  • il devrait être de type __strong id *** : c'est un pointeur vers un pointeur vers un id , qui est le même que le tableau de (array of (pointeur vers id )).

  • votre ivar doit être __strong id **** (!), parce que c'est un tableau des choses ci-dessus.
  • Si vous utilisez un multidimensional array, vous devez le faire (une dimension a été éloquée par souci de brièveté):

     - (__strong id * * *)someArray { __strong id * * *cache = (__strong id * * *)malloc(x*y*sizeof(void *)); id hello = @"Hello"; cache[0] = (__strong id * *)malloc(sizeof(void *)); //same for cache[1..x-1] cache[0][0] = &hello; // for all cache[x][y] return (__strong id * * *)cache; } 
  • correct, c'est ainsi que vous utilisez un tel pointeur.

  • oui, le tableau à deux dimensions fonctionne de la même manière, sans la première dimension.
  • Je ne pense pas, vous dissortingbuez des pointeurs d'objects __strong donc vous devriez être grand. Cela dit, nous sums à peu près à la limite de mes capacités avec ce genre de choses maintenant, donc je pourrais bien me tromper.

Répondre à ma propre question parce que cette page web m'a donné l'information manquante dont j'avais besoin. J'ai également mis en doute la réponse de Graham, car il m'a aidé à comprendre la syntaxe.

L'astuce qui me manquait est de savoir que si je veux faire reference aux éléments du tableau via la syntaxe array[1][5][2] , et que je ne connais pas les tailles de mon tableau au moment de la compilation, je peux 't juste calloc() un seul bloc de données pour cela.

La méthode la plus facile à lire (bien que la less efficace) consiste simplement à faire une boucle:

 __strong Item ****cacheItems; cacheItems = (__strong Item ****)calloc(kMaxZooms, sizeof(Item ***)); for (int k = 0; k < kMaxZooms; k++) { cacheItems[k] = (__strong Item ***)calloc((size_t)daysOnTimeline, sizeof(Item **)); for (int j = 0; j < daysOnTimeline; j++) { cacheItems[k][j] = (__strong Item **)calloc((size_t)kMaxHeight, sizeof(Item *)); } } 

J'alloue un tableau sortingdimensionnel de Item * s, Item étant une class objective-C. (J'ai bien sûr omis le code de gestion des erreurs dans cet extrait.)

Une fois que j'ai fait cela, je peux me référer à mon tableau en utilisant la syntaxe des crochets:

 cacheItems[zoom][day][heightToUse] = item; 

La page Web que j'ai liée à ci-dessus décrit également une deuxième méthode pour effectuer les allocations de memory, qui utilise seulement un appel à calloc() par dimension. Je n'ai pas encore essayé cette méthode, car celle que je viens de décrire fonctionne assez bien pour le moment.

Je penserais à une mise en œuvre différente. À less que ce soit un problème de performance démontrable (c'est-à-dire que vous l'ayez mesuré et quantifié), essayer de stocker des objects Objective-C dans des arrays C simples est souvent une odeur de code.

Il me semble que vous avez besoin d'un object conteneur intermédiaire que nous appellerons Cache pour l'instant. Une instance existera pour chaque numéro de cache, et votre object contiendra un tableau NS (Mutable) d'entre eux. Cache objects de Cache auront des propriétés pour les jours et la hauteur maximum.

L'object Cache serait plus facilement implémenté avec un object NSArray des objects, en utilisant l'arithmétique simple pour simuler deux dimensions. Votre object cache aurait une méthode -objectAtDay:Height: pour accéder à l'object par ses coordonnées.

De cette façon, il n'y a aucun besoin de s'inquiéter de la memory management, ARC le fait pour vous.

modifier

Étant donné que les performances sont un problème, j'utiliserais un tableau 1D et roulerais ma propre arithmétique pour calculer les décalages. Le type de votre variable d'instance serait:

 __strong id* myArray; 

Vous ne pouvez utiliser que des indices C à plusieurs niveaux ( array[i][j][k] ) si vous connaissez la plage de toutes les dimensions (sauf la première). C'est parce que le décalage réel est calculé comme

 (i * (max_j * max_k) + j * max_k + k) * sizeof(element type) 

Si le compilateur ne connaît pas max_j et max_k, il ne peut pas le faire. C'est précisément la situation dans laquelle vous vous trouvez.

Étant donné que vous devez utiliser une masortingce 1D et calculer les décalages manuellement, l'exemple Apple fonctionnera bien pour vous.