retainCount et collection d'objets
Philippe49
Membre
On sait bien qu'ajouter un objet dans un NSArray augmente son retainCount de 1 .
On sait également que lors de la désallocation de ce dit tableau, les objets voient leurs retainCount être décrémentés .
Maintenant si on fait un retain sur un tableau, ou un release sans que le tableau soit désalloué, les retainCount des objets ne sont pas changés.
[size=12pt]Expérience:[/size]
[size=12pt]Résultat :[/size]
% gcc pgm.m -o pgm -framework Cocoa
% pgm
Après la création de l'objet obj1 (en mode autorelease), il est retenu 1 fois
Après la création du premier tableau , il est retenu 2 fois
Après la copie du tableau dans un deuxième tableau, obj1 est retenu 3 fois
array1 ≠array2
premier objet de array1=premier objet de array2
Après retain sur le premier tableau, obj1 est toujours retenu 3 fois, , sans changement donc
Après un release sur l'un des tableaux, obj1 est retenu 3 fois, sans changement donc
Lors de la désallocation d'un des tableaux, obj1 est retenu 2 fois, avec changement cette fois
%
[size=12pt]Conséquence :[/size]
Un NSArray mal désalloué ==> tous les objets de ce NSArray ont un retainCount faux.
On sait également que lors de la désallocation de ce dit tableau, les objets voient leurs retainCount être décrémentés .
Maintenant si on fait un retain sur un tableau, ou un release sans que le tableau soit désalloué, les retainCount des objets ne sont pas changés.
[size=12pt]Expérience:[/size]
#import <Cocoa/Cocoa.h><br /><br />int main (int argc, const char * argv[]) {<br /> NSAutoreleasePool * pool=[[NSAutoreleasePool alloc] init];<br /> NSButton * obj1=[[[NSButton alloc] initWithFrame:NSZeroRect] autorelease]; <br /><br /> // Après la création des deux objets <br /> NSLog(@"\rAprès la création de l'objet obj1 (en mode autorelease), il est retenu %u fois\n",[obj1 retainCount]);<br /><br /> // On crée un tableau avec les deux objets <br /> NSArray * array1=[[NSArray alloc] initWithObjects:obj1,nil] ;<br /> NSLog(@"\rAprès la création du premier tableau , il est retenu %u fois\n",[obj1 retainCount]);<br /> <br /> // On copie le tableau dans un autre tableau<br /> NSArray * array2=[NSArray arrayWithArray:array1];<br /> NSLog(@"\rAprès la copie du tableau dans un deuxième tableau, obj1 est retenu %u fois\n",[obj1 retainCount]);<br /><br /> // les tableaux sont différents, les objets sont les mêmes<br /> NSLog(array1==array2 ? @"\r array1=array2 " : @"\rarray1 ≠array2 ");<br /> NSLog([array1 objectAtIndex:0]==[array2 objectAtIndex:0] ? @"\rpremier objet de array1=premier objet de array2\n" : @"\rpremier objet de array1 ≠premier objet de array2\n");<br /><br /> // Le retain sur le tableau n'affecte pas le retainCount de chaque objet<br /> [array1 retain];<br /> NSLog(@"\rAprès retain sur le premier tableau, obj1 est toujours retenu %u fois, , sans changement donc\n",[obj1 retainCount]);<br /><br /> // Le releaase sur le tableau n'affecte le retainCount de chaque objet que par disparition<br /> [array1 release];<br /> NSLog(@"\rAprès un release sur l'un des tableaux, obj1 est retenu %u fois, sans changement donc\n",[obj1 retainCount]);<br /><br /> [array1 release];<br /> NSLog(@"\rLors de la désallocation d'un des tableaux, obj1 est retenu %u fois, avec changement cette fois",[obj1 retainCount]); <br /> <br /> [pool drain];<br /> return 0;<br />}
[size=12pt]Résultat :[/size]
% gcc pgm.m -o pgm -framework Cocoa
% pgm
Après la création de l'objet obj1 (en mode autorelease), il est retenu 1 fois
Après la création du premier tableau , il est retenu 2 fois
Après la copie du tableau dans un deuxième tableau, obj1 est retenu 3 fois
array1 ≠array2
premier objet de array1=premier objet de array2
Après retain sur le premier tableau, obj1 est toujours retenu 3 fois, , sans changement donc
Après un release sur l'un des tableaux, obj1 est retenu 3 fois, sans changement donc
Lors de la désallocation d'un des tableaux, obj1 est retenu 2 fois, avec changement cette fois
%
[size=12pt]Conséquence :[/size]
Un NSArray mal désalloué ==> tous les objets de ce NSArray ont un retainCount faux.
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
En tous cas, excellent exemple pour mettre en évidence la nécessité d'une bonne gestion de la mémoire
Je ne vois rien d'anormal.
Le refCount de obj1 est à tout moment valide.
Il vaut 2 à la fin, car obj1 est bien retenu 2 fois : par array2, et par le releasePool (via le init suivi du autorelease).
Le refCount, peut s'assimiler au nombre de dépendance que l'objet a vis à vis de son environnement (voir l'analogie avec la laisse de chien de Hillegass).
Modifier le refCount d'une collection ne doit pas modifier les refCounts des objets contenus, car il n'y a pas de modification du nombre de dépendance de ces derniers.
PS : ce n'est pas une réponse à Philippe dont l'exemple est caractéristique (cas d'école), mais plutôt une précision pour Ancrou (cf l'autre topic).
Question : pourquoi ne pas utiliser la magnifique application qu'est xCode pour compiler ?
Pourquoi des \r et \n dans les log ??
Pourquoi ne pas faire simple ?
NSLog([array1 objectAtIndex:0]==[array2 objectAtIndex:0] ? @\rpremier objet de array1=premier objet de array2\n : @\rpremier objet de array1 ≠premier objet de array2\n);
Serait plutôt : (on évite d'écrire deux fois la même chaine de caractère !)
NSLog(@premier objet de array1 %@ premier objet de array2",[array1 objectAtIndex:0]==[array2 objectAtIndex:0]?@=:@≠);
Ben... Oui, encore heureux
Le retain count vaut max int (au pifomètre) : c'est un cas particulier.
Faire un NSString stringWithString:... mais si c'est juste pour faire la copie d'une chaine static...
Pour enfoncer un clou, on prend un marteau, pour arracher un arbre, on prend une pelle mécanique
Pour compiler xCode utilise une ligne de commande avec gcc 10 fois plus compliquée. voir ici
La présentation de Veve avec toutes les "2008-08-07 01:38:31.975 memoire_objective-cocoa.org[1230:10b]" donnent la réponse à cette question.
En quoi ce code est compliqué ?
C'est ce que te dis Supermic, tu as du mettre une chaà®ne constante comme objet, et le retain count dans ce cas est NSIntegerMax. Un retain ou un release sur un objet ayant ce retainCount est sans effet :
[size=12pt]Exemple[/size]
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool=[[NSAutoreleasePool alloc] init];
NSString * str=@Coucou;
NSLog(@\r%u                            ,[str retainCount]);
[str retain];
NSLog(@\r%u                            ,[str retainCount]);
[str release];
NSLog(@\r%u                            ,[str retainCount]);
[pool drain];
return 0;
}
[size=12pt]Exécution[/size]
% gcc pgm.m -o pgm -framework Foundation
% pgm
2147483647Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â
2147483647
2147483647Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â
%
1 et 3. Sais pas... c'est histoire d'être "moderne"... mais c'est vrai, nous ne sommes qu'en 2008... tu préfères rouler en 104 ou en 307 ? Pourquoi ne pas utiliser printf alors ? Foir directement écrire sur le stdout ?
4. NSIntegerMax tu as vérifié dans le doc ?
C'est quoi les points 1. 2. 3. 4. auquel tu réponds ? (pourquoi ne pas avoir utilisé les [ quote ] pour rendre ton post plus clair ? enfin je dis ça... juste histoire d'être moderne hein ;D) Et heu j'ai rien compris de ce que venait faire là le coup des applications localisées ou en UB ?!
Sinon pour NSIntegerMax, c'est indiqué dans la doc ceci : C'est vrai que NSIntegerMax vaut LONG_MAX et pas UINT_MAX mais bon le principal c'était l'idée, à savoir que pour les objets non autoreleasés comme les NSString statiques, ils ont un retainCount qui vaut une valeur limite spéciale, et que dans ce cas particulier retain et release n'ont aucun effet alors sur leur retainCount.
On peut même signaler qu'en mode Release, les NSLog qui resteraient dans le code s'affichent dans ce log.
printf=fprint(sdout), NSLog ça serait plutôt fprintf(stderr). Mais tu as raison, on pourrait remplacer le NSLog par un printf dans ce code. Ceci dit, si on veut garder propre la Console, il faudrait ne jamais utiliser NSLog, même dans XCode.
Il me semble que je suis tombé dessus il y a quelques semaines, et que j'avais fait déjà l'essai que j'indiques au post précédent, avec une boucle même de retain pour voir ce qui se passe si on fait un très grand nombre de retain sur un objet . J'avoue ne pas avoir et ne pas avoir eu le courage de revérifier, mais cela me semble si logique en C que ce serait pour moi une surprise que cela soit autrement, sauf réglage spécial évidemment.
Merci Ali de calmer le jeu.
Ce qui est marrant dans l'histoire , et qui confirme ce que tu dis "le principal ici c'était l'idée" énoncée en premier lieu par Supermic dans son premier post, c'est que la doc est ici en contradiction avec l'expérimentation, UINT_MAX c'est 4 milliards et quelques , ce qui est affiché dans l'expérience, c'est INT_MAX.
http://developer.apple.com/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/chapter_3_section_10.html
oui, c'était bien à cause des chaà®ne mais j'ai des chaà®nes statique (ou modifiables d'ailleurs) parce que je n'ai pus compiler avec le bouton (NSButton) : j'ai eu une erreur.
J'avais créer un nouveau projet pour cet exemple avec Xcode 3.1 (SDK Iphone) un console application.
Il y a confusion sur le mot static. En C, et donc en Objective-C, le qualificateur static n'a rien à voir avec const. C'est une question de durée de vie de la variable.
[size=12pt]Exemple :[/size]
#import <Foundation/Foundation.h>
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool=[[NSAutoreleasePool alloc] init];
static NSString * string=nil;
string=[NSString stringWithFormat:@Hello World %d,2006];
NSLog(@\rPour une chaà®ne déclarée en dynamique, le retain count de la chaà®ne est %u,[string retainCount]);
string=@Coucou;
NSLog(@\rPour une chaà®ne déclarée par référence à une chaà®ne constante, le retain count de la chaà®ne est %u,[string retainCount]);
[pool drain];
return 0;
}
% gcc pgm.m -o pgm -framework Foundation
% pgm
Pour une chaà®ne déclarée en dynamique, le retain count de la chaà®ne est 1
Pour une chaà®ne déclarée par référence à une chaà®ne constante, le retain count de la chaà®ne est 2147483647
%
NSManagedObjectContext * managedObjectContext(){
static NSManagedObjectContext * moc=nil;
if(moc) {
return moc;
}
moc=[[NSManagedObjectContext alloc] init];
// ... implementation à compléter
return moc;
}
La variable moc est créée une fois et une seule lors du premier appel de la fonction, valant initialement nil. Comme au premier appel elle vaut nil, on passe aux instructions d'initialisation.
Lors d'un prochain appel de la fonction moc n'est plus nil puisqu'il a été initialisé au premier appel, et la valeur de moc est directement renvoyée.
je comprend tres bien cela !
pardon mais je souhaité savoir pourquoi je n'arrivais pas compiler avec le NSButton.
Tu as du choisir un Template qui ne contenait pas le framework AppKit, framework qui contient la classe NSButton.
Tu parles plus haut de "Console Application" , je ne connais pas dans XCode 3.1, sans doute est-ce une Foundation Tool ou peut-être standard Tool que tu as pris ? Ces projets ne contiennent pas au départ que le framework Foundation dans lequel ne se trouve pas NSButton. Dans ce cas, il aurait été possible d'ajouter le framework Cocoa.Â
et c'est en faite un "Foundation Tool" !! ::)
Regarde dans le group framework, tu ne dois avoir que le framework Foundation. Cela veut dire que l'option de compilation ne sera que -framework Foundation et non -framework Cocoa, et par conséquent, le #import <Cocoa/Cocoa.h> ne pourra pas être réalisé.
• Faire un clic-droit sur le group "Externals Frameworks And Libraries" (pour que ton projet reste bien rangé on le met là )
• Choisir Add Existing Framework
• Le panel doit s'ouvrir dans /Systeme/Library/Frameworks
• Choisir Cocoa.framework
On peut alors supprimer Foundation (sélectionner et effacer) car Cocoa = Foundation+AppKit
Tu dois tomber sur une erreur avec cet include non ?
Si le Framework n'a pas été ajouté au projet il ne risque pas de trouver le header.