objective c et fuite memoire

sasukeguy

Membre enregistré
8 Mai 2012
6
0
Bonjour a tous,
Je suis actuellement entrain de développer sous XCode à l'aide du langage objective C. Je n'ai aucun problème avec celui_ci de cmpréhension ou d'utilisation mais pour le bon développement de mon logiciel, j’aimerais m'imposer la rigueur qu'il n'y ai aucun fuite mémoire. De ce fait j'utilise le logiciel leaks fournit avec XCode et j'ai lut quelques tutoriels.

Le grand point qui en est ressorti, c'est qu'a chaque allocation d'un variable il doit y avoir un release ou un autorelease dépendant de la manière dont la variable est détruite.

Seul souci j'applique cette régle avec rigueur mais toujours autant de fuite mémoire.

Second souci je peut créer une variable sans avoir à l’instancier avec alloc init donc je n'ai pas besoin de gérer de realease sur cette objet?

Si des imprim écrans sont demandés, je peut en envoyer.

Merci d'avance pour vos futurs réponses.
 
C'est une très bonne habitude que tu prends de gérer ta mémoire avec rigueur, même si ARC permet maintenant de s'affranchir de çà (personnellement je suis restée sans ARC). On ne peux que t'encourager à persévérer dans ce sens...

Sans code, il nous est impossible de t'aider. Si tu as un doute sur une partie de code, n'hésite pas à la mettre en copie (avec des balises code STP) ; on pourra peut-être t'aider.
 
Alors voila j'ai plusieurs erreurs de fuites mémoires. La première provenant de l'appKit, je doit surement passer de mauvais paramètre ou mal développer. Mon projet comporte des interfaces graphiques simples développer sous interface builder avec des fenêtres et Outlets. Mes fenetres étant automatiquement autorealease à la fermeture.

Lors de l'ouverture du projet une première fuite mémoire est apparut qui se situe dans mon delegate et qui provient de l'appKit.

Voici le code de mon delegate ainsi que la classe qui est appelé contenant toutes mes méthodes que j'utilise un peu partout dans mon projet. J'utilise aussi une base de donnée SQLite dans mon projet. J'ai récupérer un code marchant pour une table et je l'ai adapter a mes besoins afin de l'utiliser dans toutes les conditions.

delegate.m
Bloc de code:
//
//  projet_aditAppDelegate.m
//
//  Created by Reims_04 on 23/04/12.
//  Copyright 2012 ADIT Champagne-Ardenne. All rights reserved.
//

#import "projet_aditAppDelegate.h"

@implementation projet_aditAppDelegate

- (IBAction)ajouter_entree:(id)sender {
    
    //Methode general navigation ect..
    pool = [[general_methode alloc]init];
    
    //J'apelle la methode qui va changer de fenetre 
    [pool Navigation:@"ajout_entree" :window];
    
    [pool release];
}

- (IBAction)questionnaire_etude:(id)sender {
    
    //Methode general navigation ect..
    pool = [[general_methode alloc]init];
    
    //J'apelle la methode qui va changer de fenetre 
    [pool Navigation:@"questionnaire" :window];
    
    [pool release];
}


- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication{
    //Liberation de l'objet pour eviter fuite memoire
    return YES;
}



- (IBAction)recherche:(id)sender {
    
    //Methode general navigation ect..
    pool = [[general_methode alloc]init];
    //J'apelle la methode qui va changer de fenetre 
    [pool Navigation:@"recherche" :window];
    
    [pool release];
}

- (IBAction)admin:(id)sender {
    
    //Methode general navigation ect..
    pool = [[general_methode alloc]init];
    //J'apelle la methode qui va changer de fenetre 
    [pool Navigation:@"admin" :window];
    [pool release];
}

@end
delegate.h
Bloc de code:
//
//  projet_aditAppDelegate.h
//
//  Created by Reims_04 on 23/04/12.
//  Copyright 2012 ADIT Champagne-Ardenne. All rights reserved.
//

#import <Cocoa/Cocoa.h>
#import "general_methode.h"

@interface projet_aditAppDelegate : NSObject {
    IBOutlet NSWindow *window;
    IBOutlet id button_ajouter_entree;
    IBOutlet id questionnaire_etude;
    IBOutlet id recherche;
    IBOutlet id admin;
    general_methode *pool;
}

- (IBAction)ajouter_entree:(id)sender;
- (IBAction)questionnaire_etude:(id)sender;
- (IBAction)recherche:(id)sender;
- (IBAction)admin:(id)sender;
@end
general_methode.m
Bloc de code:
//
//  general_methode.m
//  projet_adit
//
//  Created by Reims_04 on 24/04/12.
//  Copyright 2012 ADIT Champagne-Ardenne. All rights reserved.
//

#import "general_methode.h"


@implementation general_methode

-(void) Message_info:(NSString*) message{
    
    NSAlert* msgBox = [[[NSAlert alloc] init] autorelease];
    [msgBox setMessageText: [NSString stringWithFormat:@"%@",message]];
    [msgBox addButtonWithTitle: @"OK"];
    [msgBox runModal];
    
    [msgBox release];
}



-(BOOL) Confirmation:(NSString*) message:(NSWindow*) window{

    confirm = NO;
    // Show the confirmation.
       NSAlert* msgBox = [[[NSAlert alloc] init] autorelease];
    [msgBox setMessageText: [NSString stringWithFormat:@"%@",message]];
    [msgBox addButtonWithTitle: @"Non"];
    [msgBox addButtonWithTitle: @"Oui"];
    NSInteger button =  [msgBox runModal];
    
    if(button == 1001)
        confirm = YES;
    [msgBox release];
    return confirm;
}

- (void) alertDidEnd:(NSAlert *) alert returnCode:(int) returnCode contextInfo:(int *) contextInfo
{
    *contextInfo = returnCode;
}

//Methode permettant de naviaguer c a d fermer une fenetre et en ouvrir une autre
-(void) Navigation:(NSString*)xibName:(NSWindow*)window{
    controller = [[NSWindowController alloc] 
                  initWithWindowNibName:xibName];
    if(window.isZoomed)
    {
        [controller.window performZoom:window] ; 
    }
    
    [controller showWindow:self];
    [window performClose:self];
    
}

-(NSDate *) getTodayDate
{
    //Creation NSDateFormatter to set the date format
    NSDateFormatter *formatter_default = [[NSDateFormatter alloc] init];
    [formatter_default setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
    [formatter_default setDateFormat:@"dd/MM/yyyy"];
    
    //Get today's day and return it
    NSDate* date_default = [NSDate date];
    //Liberation de la memoire
    [formatter_default release];
    return date_default;
}



-(NSDate *) getMinDate:(int)year
{
    
    //Creation NSDateFormatter to set the date format
    NSDateFormatter *formatter_default = [[NSDateFormatter alloc] init];
    [formatter_default setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
    [formatter_default setDateFormat:@"dd/MM/yyyy"];
    
    //Get today's day 
    NSDate* date_default = [NSDate date];
    //Get the min date value
    NSDate *DateMin = [date_default dateByAddingTimeInterval:(-3600)*60*24*6*year];
    //Liberation de la memoire
    [formatter_default release];
    [date_default release];
    return DateMin;
}

-(NSDate *) getMaxDate:(int)year
{
    
    //Creation NSDateFormatter to set the date format
    NSDateFormatter *formatter_default = [[NSDateFormatter alloc] init];
    [formatter_default setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
    [formatter_default setDateFormat:@"dd/MM/yyyy"];
    
    //Get today's day 
    NSDate* date_default = [NSDate date];
    //Get the min date value
    NSDate *DateMax = [date_default dateByAddingTimeInterval:3600*60*24*6*year];
    
    //Liberation de la memoire
    [formatter_default release];
    [date_default release];
    
    return DateMax;
}

-(void) dealloc{
    
    [super dealloc];
}
@end
general_methode.h
Bloc de code:
//
//  general_methode.h
//  projet_adit
//
//  Created by Reims_04 on 24/04/12.
//  Copyright 2012 ADIT Champagne-Ardenne. All rights reserved.
//

#import <Cocoa/Cocoa.h>


@interface general_methode : NSObject {
    
    IBOutlet NSWindowController *controller;
    BOOL confirm;
    int i;
}

//Method to return today's date
-(NSDate *) getTodayDate;
//Method to get Min date from today's date(calcul)
-(NSDate *) getMinDate:(int)year;
//Method to get Max date from today's date(calcul)
-(NSDate *) getMaxDate:(int)year;
//Methode permettant de naviaguer c a d fermer une fenetre et en ouvrir une autre
-(void) Navigation:(NSString*)xibName:(NSWindow*)window;
//Methode genéral pour une boite de confirmation modal 
-(BOOL) Confirmation:(NSString*) message:(NSWindow*) window;
//Message d'information prenant en compte le string
-(void) Message_info:(NSString*) message;


@end

Ce code la est générer automatiquement au lancement de l'application et j'ai des fuites mémoires mais qui n&#8217;apparaissent pas tous le temps et qui sont en référence à l'appkit. La ou je ne comprend pas c'est que certaines variables NSString initialisé correctement et dont je n'ai plus besoin après, bloque mon application lorsque je fait un release. Pourtant ces variables ne sont utilisés null part d'autres. Peut être je m'y prend mal., Merci de vos conseils et si jamais des erreurs sont présentes n&#8217;hésitez pas a m'en faire part.
 
Dernière édition:
Peux-tu poster une capture d'ecran d'Instruments avec le resultat de l'analyse des leaks?
Et si dans LeakedObject, certains ont une fleche, clique pour devlopper la liste.
 
Voici l'imprim écran que j’obtiens au démarrage de l'application. C'est assez bizarre car je n'arrive pas a situer l'erreur.
 
Je suis entrain de chercher début de l'après midi et je pense avoir cibler mon problème qui doit venir soit de l'objet pool que j'instancie et release mal ou alors de cette fenêtre NSWindowController que je ne release jamais car si je le fait la navigation ne marchera jamais.
 
apprendre a utiliser l'utilitaire de leak:rolleyes: le mec tu lui dis d'utiliser un utilitaire il l'ouvre screenshot et basta...
par utiliser "nous voulons te dire apprends a t'en servir "

selectionnes la zone du carré rouge et montres tout le stack trace et si possible aussi l'inline assembler ya toujours des gens sur cette terre qui sont de vrais devs ce n'est pas du chinois pour nous.

telecharges et installes la derniere version de clang et apprends a t'en servir et ajoutes le drapeau -pedantic quand tu compiles

http://clang-analyzer.llvm.org/

code qui pue du kuk:

c'est quoi ces pool memoires sur chaque outlet? <taquet derriere la tete> sais tu ce qu'est un pool memoire non, arrete de copier/coller du code <attrape l'oreille gauche et le traine>, les gens utilisent des pool memoires pour une certaine raison,

"The NSAutoreleasePool class is used to support Cocoa&#8217;s reference-counted memory management system. An autorelease pool stores objects that are sent a release message when the pool itself is drained."

"In a reference-counted environment (as opposed to one which uses garbage collection), an NSAutoreleasePool object contains objects that have received an autorelease message and when drained it sends a release message to each of those objects. Thus, sending autorelease instead of release to an object extends the lifetime of that object at least until the pool itself is drained (it may be longer if the object is subsequently retained). An object can be put into the same pool several times, in which case it receives a release message for each time it was put into the pool."

<coup de pied au cul ca l'aidera peut etre a agiter les trois neurones qui se battent en duel> HOP la base pas assimilée:

http://en.wikipedia.org/wiki/Memory_management
http://en.wikipedia.org/wiki/Virtual_memory
http://en.wikipedia.org/wiki/Paging
http://en.wikipedia.org/wiki/Memory_pool
https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt

apres on se demande pourquoi on s'enerve quand on voit de telle ineptie :rolleyes:

je crois qu'il va falloir que je trouve quelque chose de pire que le Bengaluru Style on touche le fond.

1/20 ( 1 pour l'effort sur la question, le prof est ironique)
 
Dernière édition:
J'ai pas lu le code en entier, je me suis arrêté à ce truc-là :

Bloc de code:
 NSAlert* msgBox = [[[NSAlert alloc] init] autorelease];
    [msgBox setMessageText: [NSString stringWithFormat:@"%@",message]];
    [msgBox addButtonWithTitle: @"OK"];
    [msgBox runModal];
    
    [msgBox release];

Pourquoi ? :(

Tu fais un alloc/init sur ta NSAlert, puis un autorelease pour la laissser "entre les mains" de la NSAutoreleasePool, mais par la suite tu fais un release dessus, c'est pas normal.

Je me suis pris la tête pendant quelques temps sur la gestion mémoire en essayant de m'en sortir seul, pour au final comprendre qu'il y a quelques trucs très simples qui suffisent à éliminer une très grandes parties des fuites.

Un principe de base :
Lorsque tu instancies un objet toi-même, via un alloc puis un init, tu es responsable de la gestion de la mémoire qu'il occupe. Tu dois donc lui envoyer un release lorsque tu en as fini avec lui.
Lorsque tu utilises un objet créé via un pattern factory (par exemple, un [NSString stringWithFormat:mad:""] utilisé dans ton exemple), tu n'est pas responsable de la gestion mémoire, l'objet retourné a été placé dans un NSAutoreleasePool, et si tu lui envoies un release, tu es à peu près sûr de te manger un bad access à tous les coups.

Autre chose, dans ton general_methode, pourquoi instancier un NSDateFormatter à chaque méthode puis le release juste après ? Ça serait plus simple de le déclarer comme variable d'instance dans le header, l'instancer une fois dans le init, puis le libérer dans le dealloc.

Enfin c'est dur d'analyser un code sans savoir à quoi il sert, même si à première vue (sans vouloir être un connard et sans agressivité aucune hein) je dirais que c'est un peu bordélique, les classes et méthodes sont mal nommées (du franglais dans les noms et commentaires, des noms de méthodes qui commencent par une majuscule, des variables d'instance non préfixées par un _...), et si il y a des fautes comme celles que j'ai relevée ci-dessus sur si peu de code, je te conseille de t'attarder un peu plus sur les bases de la gestion des objets en Obj-C.
Ne serait-ce que pour ne pas t'arracher les cheveux par la suite, car plus ton code s'étoffera, et plus tu risques de criser sur des bugs dont l'origine ne te paraîtra pas évidente.
 
merci pour ta réponse je vais me ré atteler sur le code une toute la nuit pour être sur qu'il marche et je vous renvoie une version terminée sans bug demain matin. Juste une question afin d'utiliser par exemple ma méthode de navigation qui se trouve dans mon fichier general methode. Est ce que je l'apelle bien ?? Car c'est une méthode d'instance donc je ne voit pas d'autre moyen pour l’appeler. Apres je ne suis pas sur du tout du fait de faire un release sur cet objet nommé pool de type general methode. Mais tu as raison je vais commencer par rennomer mes classes par quelque chose de plus explicit et enlever toutes les erreurs de ce type. J'ai aussi essayer clang afin de notifier les fuites mémoires. Je vous tiens au courant de mon avancemment.
 
C'est bon j'ai vérifier et revérifier mon code il n&#8217;y a plus aucune fuite mémoire d&#8217;après clang. Le problème est que mal grès ça le logiciel leaks fournit avec xcode m'indique des fuites mémoire provenant de l'appKit de NCFString et ne faisant pas plus d'un Ko. Apparemment ce sont des leaks par défaut du Kit d'apple qui se libère qu'une fois l'application fermé. Ai-je tort?

Voici le code que j'ai retoucher sans reprend les nom préfixer par _. Si il y a encore des erreurs faites moi signe, j'essaye de donner le meilleur de moi même :).

delegate.m
Bloc de code:
//
//  projet_aditAppDelegate.m
//
//  Created by Reims_04 on 23/04/12.
//  Copyright 2012 ADIT Champagne-Ardenne. All rights reserved.
//

#import "projet_aditAppDelegate.h"

@implementation projet_aditAppDelegate

- (IBAction)ajouter_entree:(id)sender {
    
    
    //Methode general de navigation de boite de dialogue et d'attribution d'une plage de date pour les NSDatePicker
    methode = [[general_methode alloc]init];
    //J'apelle la methode qui va changer de fenetre 
    [methode navigation:@"ajout_entree" :window];
    
}

- (IBAction)questionnaire_etude:(id)sender {
    
    //Methode general de navigation de boite de dialogue et d'attribution d'une plage de date pour les NSDatePicker
    methode = [[general_methode alloc]init];
        //J'apelle la methode qui va changer de fenetre 
    [methode navigation:@"questionnaire" :window];
    //[pool release];
    
}


- (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication *)theApplication{
    //Liberation de l'objet pour eviter fuite memoire
    return YES;
}



- (IBAction)recherche:(id)sender {
    //Methode general de navigation de boite de dialogue et d'attribution d'une plage de date pour les NSDatePicker
    methode = [[general_methode alloc]init];
    //J'apelle la methode qui va changer de fenetre 
    [methode navigation:@"recherche" :window];
    
}

- (IBAction)admin:(id)sender {
    
    //Methode general de navigation de boite de dialogue et d'attribution d'une plage de date pour les NSDatePicker
    methode = [[general_methode alloc]init];
    //J'apelle la methode qui va changer de fenetre 
    [methode navigation:@"admin" :window];
}

-(void) dealloc{
    [methode release];
    [super dealloc];
}

@end
delegate.h
Bloc de code:
//
//  projet_aditAppDelegate.h
//
//  Created by Reims_04 on 23/04/12.
//  Copyright 2012 ADIT Champagne-Ardenne. All rights reserved.
//

#import <Cocoa/Cocoa.h>
#import "general_methode.h"

@interface projet_aditAppDelegate : NSObject {
    IBOutlet NSWindow *window;
    IBOutlet id button_ajouter_entree;
    IBOutlet id questionnaire_etude;
    IBOutlet id recherche;
    IBOutlet id admin;
    general_methode *methode;
}

- (IBAction)ajouter_entree:(id)sender;
- (IBAction)questionnaire_etude:(id)sender;
- (IBAction)recherche:(id)sender;
- (IBAction)admin:(id)sender;
@end

general_methode.m ou methode general // A changer
Bloc de code:
//
//  general_methode.m
//  projet_adit
//
//  Created by Reims_04 on 24/04/12.
//  Copyright 2012 ADIT Champagne-Ardenne. All rights reserved.
//

#import "general_methode.h"


@implementation general_methode

//Initialisation du formatter et du booleen
-(void) initparam{
    //Booleen initialiser a non et utiliser pour la confirmation
    confirm = NO;
    //Creation NSDateFormatter pour le format de la date
    formatter_default = [[NSDateFormatter alloc] init];
    [formatter_default setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
    [formatter_default setDateFormat:@"dd/MM/yyyy"];
    
    
    
}


//Message d'information prenant en compte un NSstring
-(void) message_info:(NSString*) message{
    //Alert aec le message a utiliser pour l'alerte
    NSAlert* msgBox = [[[NSAlert alloc] init] autorelease];
    [msgBox setMessageText: [NSString stringWithFormat:@"%@",message]];
    [msgBox addButtonWithTitle: @"OK"];
    [msgBox runModal];
    
}


//Methode genéral pour une boite de confirmation modal 
-(BOOL) confirmation:(NSString*) message:(NSWindow*) window{
    //Initialisation du boolléen pour le retour de celui-ci
    [self initparam];
    // Show the confirmation.
       NSAlert* msgBox = [[[NSAlert alloc] init] autorelease];
    [msgBox setMessageText: [NSString stringWithFormat:@"%@",message]];
    [msgBox addButtonWithTitle: @"Non"];
    [msgBox addButtonWithTitle: @"Oui"];
    NSInteger button =  [msgBox runModal];
    //Si le boutton Oui est cliquer alors le booléen prend la valeur YES
    if(button == 1001)
        confirm = YES;
    return confirm;
}

- (void) alertDidEnd:(NSAlert *) alert returnCode:(int) returnCode contextInfo:(int *) contextInfo
{
    *contextInfo = returnCode;
}

//Methode permettant de naviaguer c a d fermer une fenetre et en ouvrir une autre
-(void) navigation:(NSString*)xibName:(NSWindow*)window{
    
    controller = [[NSWindowController alloc] 
                  initWithWindowNibName:xibName];
    if(window.isZoomed)
    {
        [controller.window performZoom:window] ; 
    }
    
    [controller showWindow:self];
    [window performClose:self];
    
}

//Methode qui retourne la date actuel
-(NSDate *) gettodaydate
{
    
    //Initialisation du NSDateFormatter
    [self initparam];
    
    //Récuperer la date d'aujourd'hui et la retourner
    NSDate* date_default = [NSDate date];
    
    return date_default;
}


//Methode qui retourne la date minimum fixer a A-4
-(NSDate *) getmindate:(int)year
{
    
    //Initialisation du NSDateFormatter
    [self initparam];
    
    //Récuperer la date d'aujourd'hui
    NSDate* date_default = [NSDate date];
    //Mettre la date minimum a la date actuel par defaut -4
    NSDate *DateMin = [date_default dateByAddingTimeInterval:(-3600)*60*24*6*year];

    return DateMin;
}


//Methode qui retourne la date maximum fixer a A+10
-(NSDate *) getmaxdate:(int)year
{
    
    //Initialisation du NSDateFormatter
    [self initparam];
    
    //Récuperer la date d'aujourd'hui
    NSDate* date_default = [NSDate date];
    
    //Mettre la date minimum a la date actuel par defaut +10
    NSDate *DateMax = [date_default dateByAddingTimeInterval:3600*60*24*6*year];

    
    return DateMax;
}

-(void) dealloc{
    [controller release];
    [formatter_default release];
    [super dealloc];
}

@end
Bloc de code:
//
//  general_methode.h
//  projet_adit
//
//  Created by Reims_04 on 24/04/12.
//  Copyright 2012 ADIT Champagne-Ardenne. All rights reserved.
//

#import <Cocoa/Cocoa.h>
#import "general_methode.h"

@interface general_methode : NSObject {
    //Window controller qui va servir a la navigation
    IBOutlet NSWindowController *controller;
    //Booléen pour la confirmation
    BOOL confirm;
    //NSDateFormatter pour la forme de la date
    NSDateFormatter *formatter_default;
}

//Initialisation du formatter et du booleen
-(void) initparam;

//Methode qui retourne la date actuel
-(NSDate *) gettodaydate;

//Methode qui retourne la date minimum fixer a A-4
-(NSDate *) getmindate:(int)year;

//Methode qui retourne la date maximum fixer a A+10
-(NSDate *) getmaxdate:(int)year;

//Methode permettant de naviaguer c a d fermer une fenetre et en ouvrir une autre
-(void) navigation:(NSString*)xibName:(NSWindow*)window;

//Methode genéral pour une boite de confirmation modal 
-(BOOL) confirmation:(NSString*) message:(NSWindow*) window;

//Message d'information prenant en compte un NSstring
-(void) message_info:(NSString*) message;


@end
 
Dernière édition:
Tes getTodayDate, getMinDate: et getMaxDate: font à chaque fois appel à initParam, et cette méthode instancie à chaque fois formatter_default. J'aurais pensé que ça provoquait un crash si on faisait ça sans remettre la variable à nil mais apparemment non.

Bref, plutôt qu'appeler à chaque fois initParam pour instancier formatter_default, fais plutôt ça dans ton general_method :

Bloc de code:
- (id)init {
self = [super init];

if (self)
{
    formatter_default = [[NSDateFormatter alloc] init];
    [formatter_default setTimeZone:[NSTimeZone timeZoneWithAbbreviation:@"UTC"]];
    [formatter_default setDateFormat:@"dd/MM/yyyy"];
}

return self;
}

De cette façon, dès l'instance de ta general_methode, ton formatter sera instancié lui aussi et tu auras plus à t'en occuper jusqu'à la déallocation de general_methode qui libérera aussi le formatter.
 
D'ailleurs, question bête, mais tu t'en sers où de ton NSDateFormatter en fait ?
Je vois pas d'appel à lui en dehors de la méthode qui l'instancie, tu manipules seulement des NSDate dans le reste.