2010-06-28 2 views
5

Je suis un débutant iPhone/Objective-C avec un fond Java étendu.NSMutableArray arrayWithCapacity vs initWithCapacity

J'apprends plus sur la gestion de la mémoire en c objective et je lis la documentation d'Apple sur la gestion de la mémoire: http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html

Dans la section Politique de propriété de l'objet, il est dit que vous possédez un objet que vous créez via un méthode qui commence par alloc, new ou contient copy. La propriété implique que vous devez explicitement release l'objet lorsque vous avez terminé avec elle.

Je suis à la recherche à la documentation NSMutableArray: http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSMutableArray_Class/Reference/Reference.html

Il existe deux méthodes qui font à peu près la même chose ... ils ont tous deux créer un tableau avec une capacité initiale. L'une est une méthode de classe et l'autre est une méthode d'instance.

+ (id)arrayWithCapacity:(NSUInteger)numItems; 
- (id)initWithCapacity:(NSUInteger)numItems; 

maintenant le développeur Java paresseux que je suis, pourquoi aurais-je jamais choisir la méthode d'instance sur la méthode de classe en sachant que à un moment donné dans le temps que je dois nettoyer après moi?

Je suppose que je peux manquer un point fondamental ici ... est-ce simplement une question de déterminer quand l'objet est libéré? autorelease dans la méthode de classe par rapport à release dans la méthode d'instance? Je suppose que sur une plate-forme avec des ressources très limitées (iPhone) je devrais m'abstenir d'utiliser la méthode de classe et libérer l'objet dès que j'en ai fini avec lui?

Merci!

Répondre

9

Vous choisirez généralement en fonction de si vous allez posséder l'objet pendant plus longtemps que la durée de vie de la méthode en cours (par exemple, l'affecter à un static ou directement à un ivar). Dans ce cas, vous pouvez utiliser la méthode alloc/init car vous savez que vous voulez déjà le posséder. Si vous envisagez de l'utiliser uniquement pour la portée de la méthode en cours ou si vous l'attribuez à quelque chose géré comme une propriété, vous utiliserez probablement la méthode convenience. Lorsque vous savez que vous allez posséder un objet que vous créez, l'appel alloc/init est toujours plus efficace que le mode convenience/retain puisque ce dernier est nécessaire pour allouer/init/autorelease l'objet et alors vous le retenez quand il est retourné.

Vous pouvez également utiliser les méthodes alloc/init directes lorsque vous allouez une boucle et que vous n'avez pas besoin d'un pool autorelease.

+0

Est-ce vrai maintenant que nous avons un comptage automatique des références? Il me semble que les deux méthodes doivent être à peu près identiques maintenant puisque le code ARC applique fondamentalement une auto-libération implicite à l'objet alloc/init créé maintenant. Il verrait que dans la plupart des cas, nous devrions utiliser les méthodes de commodité maintenant si cela existe pour la simplicité et la clarté, car il n'y a pas de pénalité de performance, si je comprends bien. – Mike

0

Dans ce cas, j'essaie de respecter les règles suivantes pour éviter les bugs liés à la mémoire (== « très méchant »):

  • si vous passez ce tableau en tant que paramètre, vous pouvez utiliser la méthode usine pas de problème, car il est de la responsabilité de la fonction acceptant de retenir/libérer
  • si vous voulez continuer à travailler avec l'objet, utilisez la méthode d'initialisation et libérer l'objet à la fin
6

arrayWithCapacity: a déjà l'autorelease appliquée à elle. InitWithCapacity: est explicitement conservé et vous devrez le libérer vous-même.

Puisque vous l'appelez habituellement comme [[A alloc] init ...], cela déclenchera une ampoule "J'ai besoin de gérer la mémoire pour cela", d'autres mots magiques similaires à part "alloc" étant "nouveau" et "copier" comme vous lisez dans le guide de gestion de la mémoire. Mais d'après votre question, on dirait que vous en comprenez bien les principes.

Vous avez raison de garder votre empreinte mémoire faible et gérée, mais cela ne signifie pas que vous devez toujours effectuer une initialisation/une sortie explicite. Comme Nick dit, un cas d'utilisation pour utiliser les méthodes d'usine de libération automatique est lorsque vous les passez en tant que paramètres. Un autre exemple est que lorsque vous ajoutez quelque chose à une collection comme NSDictionary ou NSArray, ce "quelque chose" peut être créé avec la méthode usine autorelease, puisque la collection "prend le relais" en le conservant. (Les choses sont conservés lorsqu'ils sont ajoutés à la collecte, et libérés lors de leur retrait.)

Vous diront que

Blah *blah = [Blah blahWithSomething]; 
[myMutableArray addObject:blah]; 

est juste plus propre à taper que

Blah *blah = [[Blah alloc] initWithSomething]; 
[myMutableArray addObject:blah]; 
[blah release]; 

Dans le premier cas, vous faites pas besoin de s'inquiéter d'un appel de libération du tout. L'inconvénient est que lorsque vous faites cela plusieurs fois dans le même runloop, l'empreinte mémoire du premier cas est plus grande SI c'est un objet tableau temporaire/jetable qui disparaît à la fin du runloop. Mais si c'est un petit objet qui n'est pas fait en boucle et qui est conservé plus longtemps, comme c'est souvent le cas, leurs empreintes sont les mêmes.

Questions connexes