autorelease précoce

chaps31chaps31 Membre
22:36 modifié dans API AppKit #1
Bonjour à  tous,

J'ai un soucis bizarre. Une méthode retourne un nsarray, juste avant le "return" je fais un [monArray autorelease];
Rien d'extraordinaire, sauf que j'ai alors un erreur d'accès mémoire... Normalement c'est comme cela que l'on fait non ? Avec l'autorelease, le release devrait avoir lieu "qu'un peu plus tard" non ?

Merci
«1

Réponses

  • CéroceCéroce Membre, Modérateur
    22:36 modifié #2
    Oui, [autorelease] place l'objet dans l'autorelease pool courant qui ne sera vidé que lors de la fin de la boucle du runtime.

    Le problème est ailleurs, montre-nous ton code.
  • schlumschlum Membre
    22:36 modifié #3
    Tu vas te décider un jour à  passer clang pour dénouer ton sac de noe“uds et comprendre tes erreurs ?  >:)
  • ClicCoolClicCool Membre
    octobre 2009 modifié #4
    Bonsoir,

    Comment Alloues-tu ton NSArray ?

    Est-ce avec un [[NSArray alloc] Init ....?
    Ou avec un constructeur de commodité genre [NSArray arrayWith... ?

    Par ce que dans le premier cas t'as un objet avec un retainCount à  1 (que tu peux (doit?) autoreleaser)
    Dans l'autre cas t'as un objet déjà  autoreleasé et donc, ne surtout pas le releaser en plus.


    Sinon t'as raison, pour une valeure de retour d'une méthode, la convention des méthodes veut que les valeurs de retour soit autoreleasées...

    dans 1256500012:
    .../... Une méthode retourne un nsarray, .../...

    Et justement cette méthode, si elle est faite dans les règles de l'art, te retourne un objet "autoreleasé" donc ne pas y ajouter un autorelease de plus mal venu ....

  • ClicCoolClicCool Membre
    22:36 modifié #5
    dans 1256500835:

    Tu vas te décider un jour à  passer clang pour dénouer ton sac de noe“uds et comprendre tes erreurs ?  >:)


    Je plussoie.
  • chaps31chaps31 Membre
    22:36 modifié #6
    J'ai passé Clang, bon faut que je me mette à  gérer la mémoire, le résultat fait peur.... J'ai bien fais un init et potassé la gestion de la mémoire avant sur les méthode de classe qui "appartiennent" à  la classe qui les a créé et donc qui sont "autoreleasé" par la classe.
    -(NSMutableArray *)createTabComplete:(NSString *) table:(NSString *) orderby<br />{<br />	NSString *strSelect=[[NSString alloc] initWithFormat:@&quot;SELECT * FROM %@ %@&quot;,table,orderby];<br />	PGSQLRecordset *rs = [connection open:strSelect];<br />	[strSelect release];<br />	if (rs==nil &amp;&amp; [table isEqualTo:@&quot;utilisateurs&quot;]) <br />		{ <br />			[AlertMes shutdown:@&quot;Erreur&quot; :@&quot;Table utilisateur vide - Gestovet doit quitter.&quot;];<br />		}<br />	NSMutableArray *tabRetour=[[NSMutableArray alloc]init];	//TableauSource en retour<br />	while (![rs isEOF])<br />		{<br />			NSMutableDictionary *dicoForTab=[[NSMutableDictionary alloc] initWithDictionary:[[rs dictionaryFromRecord]mutableCopy]];<br />			NSEnumerator *enumKeys=[[[rs dictionaryFromRecord]mutableCopy] keyEnumerator]; <br />			<br />			id identifier; <br />			while ( (identifier = [enumKeys nextObject]) )&nbsp; <br />				{<br />					[dicoForTab setValue:[NSMutableString stringWithString:[self reinterpretAsUTF8:[dicoForTab valueForKey:identifier]]] forKey:identifier];<br />				}<br />			[tabRetour addObject:dicoForTab];<br />			[dicoForTab release];<br />			[rs moveNext];<br />		}<br />//ICI AUTORELEASE DE TABRETOUR QUI PLANTE<br />	return tabRetour;<br />}
    


    Si je mets l'autorelease  à  l'emplacement indiqué, PAF "EXEC_BAD_ACCESS"...
  • schlumschlum Membre
    22:36 modifié #7
    Je mise sur l'effet de bord d'un autre problème...
  • chaps31chaps31 Membre
    22:36 modifié #8
    J'ai mis des autorelease ailleurs (avouons le je ne me suis pas posé le pb de la mémoire jusque là ) ... ET systématiquement ça crée des problèmes alors que ça ne devrait pas.

    Des méthodes de 3 lignes :
    Ligne 1 alloc init
    Ligne 2 appel à  la méthode dont je parle dans ce threat qui retourne une valeur prise par l'objet de la ligne 1
    ligne 3 retour de la valeur prise par l'objet de la ligne 2

    Ben dès que je met un autorelease que ce soit juste avant le return ou bien sur la ligne du alloc init j'ai un soucis, message d'erreur, ou encore compilation interminable qui bloqie mon macbook...
  • chaps31chaps31 Membre
    22:36 modifié #9
    Etant amateur sorti de la programmation PHP et suite à  une remarque d'Ali, je me demandais, est-ce correct ?
    -(NSMutableArray *)bddReadCpVille<br />{<br />	NSMutableArray *cpVilles=[[NSMutableArray alloc] init];<br />	cpVilles=[self createTabComplete:@&quot;cpville&quot;:@&quot;ORDER BY ville,cp&quot;];<br />	return cpVilles;<br />}
    

    Ou bien est-ce ça qui est correct :
    NSMutableArray *cpVilles=[[NSMutableArray alloc] initWithArray:[self createTabComplete:@&quot;cpville&quot;:@&quot;ORDER BY ville,cp&quot;]];
    
  • schlumschlum Membre
    22:36 modifié #10
    Le premier n'est pas correct... D'ailleurs "clang" doit t'engueuler sévèrement pour ça  :P
    - Tu alloues un NSMutable array
    - Tu perds la référence -> leak
  • chaps31chaps31 Membre
    22:36 modifié #11
    C'est en effet clang qui m'a mis la puce à  l'oreille ;) 
    Tu veux dire qu'un espace mémoire est alloué lors de l'alloc init, mais aussi à  la deuxième ligne ?
  • schlumschlum Membre
    22:36 modifié #12
    dans 1256556591:

    C'est en effet clang qui m'a mis la puce à  l'oreille ;) 
    Tu veux dire qu'un espace mémoire est alloué lors de l'alloc init, mais aussi à  la deuxième ligne ?


    Bah à  la 2e ligne je ne sais pas ce que fait ta fonction, mais en tout cas l'espace mémoire alloué à  la première ligne est perdu à  tout jamais...
  • AliGatorAliGator Membre, Modérateur
    22:36 modifié #13
    C'est un peu comme si tu faisais [tt]a = 5;[/tt] puis [tt]a = 2[/tt] après, du coup ton 5 est perdu, écrasé par le 2.
    Ou comme si tu faisais en PHP ou JS ou autre, genre [tt]t = new Array();[/tt] puis [tt]t = retourneTableau();[/tt], la valeur retournée par retourneTableau remplace la précédente valeur, donc le précédent tableau créé par [tt]new Array()[/tt], ce tableau créé par new Array ne sert donc à  rien puisqu'il n'est jamais utilisé et immédiatement remplacé et donc perdu !
  • schlumschlum Membre
    22:36 modifié #14
    Le PHP n'est pas un très bon exemple...  :)
    Je ne sais pas si on peut créer un leak avec... mais en tout cas on peut faire un peu n'importe quoi.
  • wiskywisky Membre
    octobre 2009 modifié #15
    dans 1256562038:

    Le PHP n'est pas un très bon exemple...  :)
    Je ne sais pas si on peut créer un leak avec... mais en tout cas on peut faire un peu n'importe quoi.

    C'est prévu pour eviter le problème. De même que les variables sont très peux typé ! C'est pas le langage le plus approprié pour apprendre à  gérer la mémoire.



    PS : question HS : comment supprimer un mot ajouté dans le dictionnaire de FireFox par erreur ?
  • AliGatorAliGator Membre, Modérateur
    22:36 modifié #16
    Rôôô mais je sais bien, je prenais le PHP en exemple parce que chaps vient de ce langage, donc pour faire le parallèle non pas de "en PHP aussi t'aurais un leak mémoire" mais de "si tu crées ton nouvel objet alors que tu lui affectes une autre valeur après, ça sert à  rien puisque le nouvel objet est remplacé par le nouveau donc perdu..." :P
  • chaps31chaps31 Membre
    22:36 modifié #17
    Hé oui, le PHP et la mémoire... on s'en fout...
    Toute ces histoires me font me poser une question.

    Soit une variable d'instance d'une de mes classes :
    NSMutableArray *maVar;

    Dans mon code écrire juste :
    maVar=[unReceveur methodeRetournantUnTableau];
    Suffit

    Pas besoin de alloc init.

    tout autant que pour une variable propre à  une méthode, je peux écrire.
    NSString *maString;
    maString=[nReceveur methodeRetournantUnString];
    Pas de alloc Init
  • schlumschlum Membre
    22:36 modifié #18
    Ben oui... ce sont des pointeurs...

    D'ailleurs on écrit généralement ça en une ligne :
    NSString *maString = [nReceveur methodeRetournantUnString];
    
  • chaps31chaps31 Membre
    22:36 modifié #19
    Bon ben c'est pas encore demain que je vais quitter le forum débutant... ::)

    Du coup "villes" étant un tableau variable d'instance de ma classe :

    villes=[[NSMutableArray alloc]initWithArray:[cpVilles valueForKey:@&quot;ville&quot;]];<br />villes=[self eraseRedond:villes];
    


    Avec eraseRedond qui vire du tableau les doublons,c'est pas terrible, vaut mieux que eraseRedond ne renvoie rien (void), il modifie ce que je lui passe (la variable d'instance) et je réécris :

    villes=[[NSMutableArray alloc]initWithArray:[cpVilles valueForKey:@&quot;ville&quot;]];<br />[self eraseRedond:villes];
    
  • AliGatorAliGator Membre, Modérateur
    22:36 modifié #20
    Oui alors en fait pas tout à  fait. Côté équilibre des alloc/init avec les (auto)release, c'est bon, donc tu peux te dire que tu n'auras pas de fuite mémoire.

    Cependant la grande nuance entre une variable d'instance et une variable propre à  une méthode, c'est que la variable d'instance elle existe pendant toute la durée de vie de l'objet, là  où la variable propre à  la méthode n'existe que dans le contexte du bloc de ta méthode et n'est plus accédee après.

    Du coup, si tu respectes les règles, normalement [tt][unReceveur methodeRetournantUnString][/tt] est sensé te retourner un objet autoreleased, que tu stoques dans maVar ou maString... oui mais un objet autoreleased est relâché "plus tard"... c'est à  dire plus précisément à  la fin de la runloop.
    Ca va très bien pour des objets créés dans ta méthode, et passés de main en main dans les méthodes appelantes ou appelées, jusqu'à  ce que toute la chaà®ne des méthodes consécutives à  ton événement (un tap/clic sur un bouton, une méthode de delegate, ou autre, qui a son tour a généré tous les appels de méthodes etc). Mais pour une variable d'instance, le but de ces dernières étant de stocker des objets/variables pendant toute la vie de l'objet/l'instance... là  par contre tu es sensé pouvoir y accéder plus tard aussi.
    Et si tu les gardes en "autorelease", elles vont être détruites bien trop tôt.


    Conclusion, pour les variables d'instances, il faut garder les objets que tu mets dans ces variables d'instance en mémoire tant que l'instance en question existe. Donc par exemple pour les variables d'instance que tu initialises dès la création de ton objet, dans le init / initWithCoder / initMachinTruc, il faut que ces variables aient encore un retainCount de 1 à  la fin de l'init. Et tu équilibre avec un release, certes, mais dans le -(void)dealloc de ta classe, qui est un peu le pendant / symétrique du "dealloc", si tu veux. Si ce sont des variables que tu n'initialises que plus tard, ça ne change rien, il faut pour ces variables d'instance faire le release dans le dealloc (si la variable n'a jamais eu l'occasion d'être initialisée pendant la durée de vie de l'objet, tu enverras alors un release à  un objet "nil", ce qui n'a aucune conséquence, et si ça a été initialisé à  un objet dont le retainCount est donc à  1, ça va bien le libérer.
  • AliGatorAliGator Membre, Modérateur
    22:36 modifié #21
    Oui, pour ta méthode eraseRedond, si son but est d'effacer les entrées redondantes, il n'y a pas lieu vu le nom de la méthode de retourner un autre NSArray.
    D'après les conventions de nommage, soit tu appelles ta méthode "arrayWithDuplicatedRemoved" ou un truc comme ça, qui, comme son nom commence par "array..." mentionne que ça retourne un nouveau tableau autoreleased, soit tu gardes le nom "eraseRedond" ou "eraseDuplicates" mais alors d'après le nom ça effectue l'action d'effacer les doublons... sur l'objet courant, sans en retourner un nouveau mais bien en modifiant l'objet.


    Bon, maintenant, pour des collections n'ayant pas la vocation d'avoir des doublons, plutôt qu'utiliser un NSArray tu pourrais voir si ça ne vaut pas le coup d'utiliser un NSSet (un "set" = un "ensemble" (y compris au sens mathématique du terme = collection sans ordre et sans doublon). C'est p'tet pas mieux que de supprimer les doublons, selon les méthodes dont tu auras besoin pour manipuler tes villes ensuite (genre si justement tu veux un certain ordre pour ce tableau "villes"), à  toi de voir.
    Ou sinon aussi un NSDictionary, où les clés sont les noms des villes, et les objets associés peuvent être alors des objets persos de la classe perso "Ville" par exemple, classe contenant toutes les informations d'une ville. Ou alors l'objet associé est ton objet cpVilles (celui dont tu extraits le nom de la ville pour déterminer la clé justement)... Enfin à  réfléchir selon ton cas, ce ne sont que des pistes, à  toi de voir selon ton usage.
  • chaps31chaps31 Membre
    22:36 modifié #22
    Merci beaucoup, fort instructif
  • chaps31chaps31 Membre
    22:36 modifié #23
    Clang ne se gourre pas parfois, comme lorsqu'il dit qu'il y a une fuite mémoire là  :
    [majdata setValue:[NSNumber numberWithInt:[cpClt intValue]] forKey:@&quot;cp&quot;];
    


    ou là 
    [majdata setValue:adresmut&nbsp; forKey:@&quot;adresse&quot;];
    
  • schlumschlum Membre
    22:36 modifié #24
    dans 1256579244:

    Clang ne se gourre pas parfois, comme lorsqu'il dit qu'il y a une fuite mémoire là  :
    [majdata setValue:[NSNumber numberWithInt:[cpClt intValue]] forKey:@&quot;cp&quot;];
    


    ou là 
    [majdata setValue:adresmut&nbsp; forKey:@&quot;adresse&quot;];
    



    Là , je dirais que t'as pas compris le rapport de clang  :P
  • ClicCoolClicCool Membre
    22:36 modifié #25
    Bon, ça fait un moment que ça me démange, et, à  chaque notif de réponse à  ce sujet j'y pense en lisant le titre, alors voilà :

    J'ai connu quelqu'un qui avait un problème d'Autorelease précoce.
    Il a été voir un psychiatre (psy-sexologue) et depuis sa femme est très épanouie :)





    :)beta:
    Me cherchez pas, je suis déjà  sorti
  • muqaddarmuqaddar Administrateur
    22:36 modifié #26
    ça va Hervé ?
    La forme, le toubib...  :kicking:  ;D
  • chaps31chaps31 Membre
    22:36 modifié #27
    dans 1256580878:

    Bon, ça fait un moment que ça me démange, et, à  chaque notif de réponse à  ce sujet j'y pense en lisant le titre, alors voilà :

    J'ai connu quelqu'un qui avait un problème d'Autorelease précoce.
    Il a été voir un psychiatre (psy-sexologue) et depuis sa femme est très épanouie :)



    Ha... je me demandais qui allait rebondir sur le titre  ;)

    Pour clang je vois "Leak" je pense fuite mémoire ce qui semblait se confirmer à  d'autres endroits du code.
  • AliGatorAliGator Membre, Modérateur
    22:36 modifié #28
    Ce que voulais dire schlum c'est que peut-être as-tu mal identifié la cause ou l'emplacement : la leak vient peut-être de plus haut dans le code, ou est une conséquence d'un code dépendant, par exemple.
  • ClicCoolClicCool Membre
    22:36 modifié #29
    Par exemple, dans ce message de Clang:
    /Users/ClicCool/Developpements/myApp/myApp_AppDelegate.m:121:1 Potential leak of an object allocated on line 117


    Qui apparaà®t ligne 121, l'allocation litigieuse se situe au dessus, ligne 117 de mon code de l'AppDelegate.
  • schlumschlum Membre
    22:36 modifié #30
    Ce que je voulais dire c'est qu'il t'indique probablement la dernière référence d'un objet que tu as oublié de relâcher (et qui a donc été alloué plus haut !)...
  • chaps31chaps31 Membre
    22:36 modifié #31
    Hop hop, j'ai trouvé, ce n'est pas mon dico qui lui déplaisait, mais les NSString qui servaient à  le remplir, oubli d'autorelease sur celles-ci...
    Et c'était le dernier endroit de la méthode où je les utilisais, comme quoi quand Schlum dit un truc...  ;)
Connectez-vous ou Inscrivez-vous pour répondre.