Pourquoi mon programme plante !!??

zenxzenx Membre
13:22 modifié dans API AppKit #1
J'avais déjà  posé un message similaire sur la question que je souhaite aborder ici. Si je me permets de revenir sur le sujet, c'est parce que je n'ai toujours pas compris l'interêt du [date retain] dans le listing que je vous présente ci-dessous. En effet, lorsque je le supprime, le programme plante à  la fin de l'éxecution. Voyez plutôt :

ParticipationLoterie.m

#import "ParticipationLoterie.h"


@implementation ParticipationLoterie

- (void)setNumbersRandomly {
firstNumber = random() % 100 + 1;
secondNumber= random() % 100 + 1;
}

- (void)setEntryDate: (NSCalendarDate *) date {
[date retain];
[entryDate release];
[date setCalendarFormat: @%d %b %y];
entryDate = date;
}

- (NSCalendarDate *) entryDate{
return entryDate;
}

- (int)firstNumber {
return firstNumber;
}
- (int)secondNumber {
return secondNumber;
}

- (void)dealloc {
NSLog(@Destruction %@",self);
[entryDate release];
[super dealloc];
}

- (NSString *)description{
return [NSString stringWithFormat: @%@ = %d et %d",entryDate,firstNumber,secondNumber];
}

@end

main.m

#import <Foundation/Foundation.h>
#import "ParticipationLoterie.h"

int main (int argc, const char * argv[]) {

NSMutableArray *array;
int i;
ParticipationLoterie *entry;

NSCalendarDate *now;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

now = [[NSCalendarDate alloc] init];
srandom(time(NULL));
array = [[NSMutableArray alloc] init];
for (i=0;i<10;i++){
entry = ParticipationLoterie alloc] init];<br /> [entry setNumbersRandomly];<br /> [color=blue][b][entry [color=orange]setEntryDate[/color]: [now dateByAddingYears: 0 months:0 days:(i*7) hours:0 minutes:0 seconds:0;[/b][/color]
[array addObject:entry];
[entry release];
}

NSLog(@Array = %@",[array description]);
[array release];
[now release];
[pool release];
return 0;
}

Qu'en pensez vous ?

Réponses

  • AliGatorAliGator Membre, Modérateur
    décembre 2005 modifié #3
    Au passage je ne trouve pas très propre de rajouter du code comme ça dans le main.m !

    Moi perso je n'ai jamais touché au fichier main.m qui, pour moi, doit rester en l'état.
    Si tu as des initialisations à  faire, il faut les faire dans le awakeFromNib pour un objet instancié dans IB (un contrôlleur par exemple, ou une NSView personnalisée que tu auras mis dans IB avec un outlet pointant sur elle, etc), ou dans le init ou initWithCoder pour des objets autres, et tout ce genre.

    ----

    Sinon pour l'histoire de ton retain, lis l'article de Renaud il te sera très enrichissant. Pour faire bref, dans ton cas précis, peut-être que ce remaniement de code (qui n'est pas correct, tu comprendras la subtilité dans un 2e temps, mais qui est une étape intermédiaire pour comprendre) t'aidera à  capter ?
    <br />- (void)setEntryDate: (NSCalendarDate *) date {<br />&nbsp;  // but général de ce bout de code : remplacer l&#39;ancienne valeur de entryDate par<br />&nbsp;  // une nouvelle valeur (cette passée en paramètre, &quot;date&quot;)<br /><br />&nbsp;  [date setCalendarFormat: @&quot;%d %b %y&quot;]; // on formatte juste la date comme on veux, ça c&#39;est un détail<br /><br />&nbsp;  [entryDate release]; // si la variable d&#39;instance entryDate avait une valeur avant,<br />&nbsp;  //alors on relâche cette valeur car on va la remplacer par une autre*<br />&nbsp;  entryDate = date; // la variable d&#39;instance entryDate prend la nouvelle valeur qu&#39;on veut lui donner<br />&nbsp;  [date retain]; // signaler qu&#39;on utilise cette valeur, pour ne pas risquer de la perdre<br />&nbsp;  // ne pas risquer qu&#39;ele disparaisse de la mémoire tant qu&#39;on ne la relâche pas avec un release plus tard<br /><br />&nbsp; &nbsp; // * NB : sui entryDate n&#39;avait pas de valeur (entryDate = nil) c&#39;est pas grave,<br />&nbsp; &nbsp; // car en Cocoa envoyer un message à  nil n&#39;a pas d&#39;incidence et ne fait rien.<br />}
    

    Le principe de base : les objets sont mis dans une zone de la mémoire, et les variables pointent sur ces zones mémoire. 2 variables peuvent pointer sur exactement le même objet, et changer l'objet par l'intermédiaire d'une variable fera que si tu accèdes à  ce même objet par l'autre variable ses propriétés auront changé aussi (puisque c'est le même objet).

    Maintenant, à  chaque fois que tu as une variable qui pointe sur un objet, il faut que tu fasses un retain dessus pour déclarer qu'il y a une variable de plus qui a "besoin" de cet objet. et un "release" à  chaque fois que tu n'as plus besoin de ta variable pour dire. Quand le retainCount atteint zéro c'est que plus aucune variable n'a besoin de (ou pointe vers) l'objet, donc que l'objet ne sert plus à  rien, il est alors libéré de la mémoire.

    ----

    Bon j'en ai déjà  trop dit puisque tout ça est repris dans le tuto de Renaud.

    Quant à  la petite subtilité, c'est qu'en réalité il faut dans ce genre de cas (cas classique de remplacement de la valeur d'une variable d'instance avec une méthode setXXX, elles sont typiquement toutes la même tête) toujours faire le [parametre retain] avant le [variable_d_instance release] et non pas l'inverse au cas où la variable d'instance et le paramètre pointeraient déjà  exactement sur le même objet. M'enfin tu comprendras cette subtilité quand tu maà®triseras + la gestion de la mémoire ;)
  • BruBru Membre
    13:22 modifié #4
    dans 1133434087:

    J'avais déjà  posé un message similaire sur la question que je souhaite aborder ici. Si je me permets de revenir sur le sujet, c'est parce que je n'ai toujours pas compris l'interêt du [date retain] dans le listing que je vous présente ci-dessous. En effet, lorsque je le supprime, le programme plante à  la fin de l'éxecution.


    La méthode dateByAddingYears:months:days:hours:minutes:seconds: que tu appliques à  ton objet NSCalendarDate now renvoie un nouvel objet NSCalendarDate qui est le résultat de l'addition de now avec les paramètres de la méthode.

    Ce nouvel objet NSCalendarDate n'a pas été crée par toi via alloc, donc, il s'agit d'un objet autoreleasable. Il est donc créé avec un compteur de référence valant 1, puis est mis dans l'autorelease-pool.

    Par la suite, tu fais un [tt][array release];[/tt] pour détruire le tableau contenant les entry. On peut donc supposer que chaque entry est supprimé : la méthode dealloc de la classe PartcipationLoterie est donc appellée.

    Dans dealloc, tu releases la date qui avait été précédemment récupérée du nouvel objet NSCalendarDate.

    Donc, le compteur de référence de ce nouvel objet passe de 1 à  0, ce qui a pour effet de détruire ce nouvel objet.

    Enfin, tu fais un [tt][pool release];[/tt]. En faisant cela, chaque objet contenu dans l'autorelease-pool reçoit un release.

    Comme, le nouvel objet NSCalendarDate était dans l'autorelease-pool, il reçoit son release. Mais ça ne peut que planter, puisque ce nouvel objet a été précédemment détruit.

    .
  • zenxzenx Membre
    13:22 modifié #5
    Encore un grand merci à  vous tous pour vôtre réactivité et vos explications !! Merci ! 
  • elfelf Membre
    13:22 modifié #6
    @Ali: oui effectivement c'est pas bien d'ajouter du code dans main.m sauf que ici c'est un "foundation tool" donc tu es obligé, c'est un tuto de P:Omega
Connectez-vous ou Inscrivez-vous pour répondre.