Fonction Récursive : mutliplication des threads

pierre68314pierre68314 Membre
02:36 modifié dans Xcode et Developer Tools #1

Hello,

j'ai une fonction récursive du genre :
<br /><br />- (int) fonctionRecursiveWithNewTable:(NSMutableArray *) uneTable {<br /><br />&nbsp; &nbsp;  // création d&#039;un nouveau tableau : NouvelleTable...<br /><br />&nbsp; &nbsp; //&nbsp; On place dans NouvelleTable des objets créés à  partir de uneTable<br /><br />&nbsp; &nbsp; // on libère uneTable avec un release<br /><br />&nbsp; &nbsp; // Si objectif atteint on sort de la fonction<br />&nbsp; &nbsp; if ( objectif ) return 1;<br /><br />&nbsp; &nbsp; // Sinon on repart pour un tour<br />&nbsp;  return [self fonctionRecursiveWithNewTable:NouvelleTable];<br /><br />}<br /><br />


Ma question est la suivante :
A chaque fois que la fonction "boucle", j'ai un nouveau THREAD qui apparaà®t dans XCODE.

En tant que gros gros débutant, je me demandais si cela ne signifiait pas une fuite de mémoire puisque le thread ne disparait pas.

Je croyais que le Thread était un processus qui une fois terminé disparaissait.


merci d'avance pour vos réponses

Réponses

  • CéroceCéroce Membre, Modérateur
    02:36 modifié #2
    dans 1305189630:

    j'ai une fonction récursive du genre :

    Ce n'est pas une fonction, mais une méthode.

    dans 1305189630:

    A chaque fois que la fonction "boucle", j'ai un nouveau THREAD qui apparaà®t dans XCODE.


    C'est bizarre. Un thread n'est créé que si ton programme le demande ou en appelant certaines API (par exemple NSURLConnection pour un téléchargement asynchrone).
    Mais sans le code... je ne peux pas dire.

    dans 1305189630:

    Je croyais que le Thread était un processus qui une fois terminé disparaissait.


    Non. Un processus est un programme. Le plus simple pour comprendre, c'est de taper la commande [tt]top[/tt] sur la ligne de commande. Ce sont les processus qui sont listés.
    Un thread appartient à  un processus. Le processus et ses threads partagent le même espace mémoire. Le thread fournit une boucle de calcul séparée.
  • pierre68314pierre68314 Membre
    02:36 modifié #3
    hello,


    Voilà  ma méthode pour avis

    <br /><br />- (int) remplissageWithNbpoints:(int)nb_points <br />						withListX:(NSMutableArray *)listX <br />						withListY:(NSMutableArray *)listY <br />						withBX:(NSInteger)Bx // point arrivé<br />						withBY:(NSInteger)By // point arrivé<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  withDX:(NSInteger)Dx<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  withDY:(NSInteger)Dy&nbsp; <br /><br />{<br /><br />/* coordonnée des points */<br />NSMutableArray * new_listX = [[NSMutableArray alloc] init]; // Va contenir des NSNumber<br />NSMutableArray * new_listY = [[NSMutableArray alloc] init];<br /><br />// Nombre de points<br />int new_nb_points=0;<br /><br />NSLog(@&quot;&#092;n&#092;nIl y a %d points a tester&#092;n&#092;n&quot;, [listX count] );<br />/*------------------------------------------------------------------*/<br />/* ALGO de recherche de chemin&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; */<br />/*------------------------------------------------------------------*/<br />int i, aX, aY, zX, zY;<br />&nbsp; &nbsp; for(i=0;i&lt;nb_points;i++)<br />&nbsp; &nbsp; {<br />		aX = [[listX objectAtIndex:i] intValue];<br />		aY = [[listY objectAtIndex:i] intValue];<br />		NSLog(@&quot;&#092;n&#092;n Point %dx%d : %d sur %d &#092;n&#092;n&quot;, aX, aY, i+1, [listX count] );<br />		<br />&nbsp; &nbsp; &nbsp; &nbsp; zX = aX+1;<br />&nbsp; &nbsp; &nbsp; &nbsp; zY = aY;<br />&nbsp; &nbsp; &nbsp; &nbsp; if ( ( [self test_caseWithX:zX withY:zY withDX:Dx withDY:Dy] ) ) // on fait +1 dans la case de droite<br />&nbsp; &nbsp; &nbsp; &nbsp; {		<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [new_listX addObject:[NSNumber numberWithInt:zX]];<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; [new_listY addObject:[NSNumber numberWithInt:zY]];	<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Tile * t = [[row objectAtIndex:zX] objectAtIndex:zY];<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; Tile * p = [[row objectAtIndex:aX] objectAtIndex:aY];<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; t.valeurCase = p.valeurCase+1;<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; new_nb_points++;<br />&nbsp; &nbsp; &nbsp; &nbsp; }<br />&nbsp; &nbsp; &nbsp; &nbsp; <br />&nbsp; &nbsp; &nbsp; &nbsp; // ...&nbsp; &nbsp;  <br />&nbsp; &nbsp; &nbsp; &nbsp; <br />	&nbsp;  <br />&nbsp; &nbsp; }<br />	&nbsp;  <br />	// on libère la mémoire<br />	[listX release];<br />	[listY release];<br />&nbsp; &nbsp; <br /><br />&nbsp; &nbsp; <br />	// on teste&nbsp; le point en question pour savoir si l&#039;on est arrivé au point final<br />	if ( aX == Bx &amp;&amp; aY == By || new_nb_points==0) {<br />		NSLog(@&quot;Sortie de la boucle, le chemin est trouve&quot;);<br />&nbsp; &nbsp; &nbsp; &nbsp; return 1; <br />	}<br />	else {<br />&nbsp; &nbsp; &nbsp; &nbsp; NSLog(@&quot;&#092;n&#092;nOn retourne dans la boucle&#092;n&#092;n&quot;);<br />		return [self remplissageWithNbpoints:new_nb_points<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  withListX:new_listX<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  withListY:new_listY <br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; withBX:Bx // point arrivé<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; withBY:By // point arrivé<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; withDX:Dx<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; withDY:Dy];&nbsp; <br />						<br />	}<br />}<br />	&nbsp; <br />
    

  • AliGatorAliGator Membre, Modérateur
    02:36 modifié #4
    Ouch le "release" sur un objet dont on n'est pas possesseur ! >:)
    Belle horreur la gestion mémoire dans ton code !
  • pierre68314pierre68314 Membre
    mai 2011 modifié #5
    ah ? je croyais en être le possesseur parce que je l'ai créé en faisant :

    <br /><br />NSMutableArray * ListX = [[NSMutableArray alloc] init]; <br />NSMutableArray * ListY = [[NSMutableArray alloc] init];<br /><br />
    



    et ensuite j'appelle la méthode :

    <br /><br />[self&nbsp; remplissageWithNbpoints:1<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; withListX:ListX<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; withListY:ListY<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; withBX:8<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; withBY:2<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  withDX:1<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  withDY:4];&nbsp; <br /><br />
    





    j'ai bien relu le site du zéro et je ne pense pas avoir mal agit avec la mémoire.

    info venant du tuto que je trouve clair : http://www.siteduzero.com/tutoriel-3-203151-gerer-la-memoire.html


    Faisons à  présent la différence entre un objet dont vous êtes le propriétaire et un objet dont vous ne l'êtes pas. Voici en exemple la création de deux NSString :

    Code : Objective-C - Sélectionner



    NSString * chaine_1 = [[NSString alloc] initWithString:@Vous êtes le propriétaire];
    NSString * chaine_2 = [NSString stringWithString:@Vous n'êtes pas le propriétaire];


    Comme vous le voyez, on crée chaine_1 avec la méthode + alloc, on en est donc le propriétaire. Il va donc falloir envoyer release à  cet objet, mais surtout pas à  chaine_2. ;)


    Je suis dans le cas 1, donc je libère. Qu'est-ce que j'ai manqué?
  • AliGatorAliGator Membre, Modérateur
    02:36 modifié #6
    Ce que tu as manqué, c'est qu'il faut balancer retain et release dans le mêle "scope" (la portée de la variable).

    Tu as créé l'objet dans une méthode, et tu le libères dans une autre méthode à  laquelle tu as passé cet objet en paramètre.

    Il faut toujours que ce soit celui qui fait le alloc/copy/retain qui fasse le release/autorelease correspondant. Par "celui qui", j'entend :
    • Si c'est une variable locale à  un scope (par exemple variable locale à  une fonction, ou variable déclarée à  l'intérieur d'un if...), dans le même scope (donc au "même niveau d'accolades { }")
    • Si c'est une variable dont la portée est la classe, typiquement une variable d'instance, évidemment si tu fais le "alloc" dans le constructeur tu ne fais pas le release dans le constructeur, mais bien dans le destructeur


    Là  ce que tu fais c'est que tu fais un alloc/init d'un objet que tu stockes dans une variable locale, listX, dans une méthode A, puis tu passes cette variable en paramètre à  une méthode B et fait le release dans cette méthode B. C'est totalement contraire aux règles de base de la gestion mémoire. C'est celui à  qui appartient la variable (donc la méthode A, qui est la portée de la variable locale dans laquelle tu as stocké ton objet créé par alloc/init) qui doit gérer la mémoire de la variable.


    Tu es peut-être embrouillé par le fait que tu as nommé ta variable locale dans la méthode A et le paramètre dans la méthode [tt]remplissageWithNbpoints:withListX:withListY:withBX:withBY:withDX:withDY:[/tt] avec exactement le même nom. Tu aurais mis des noms différents cela t'aurais peut-être plus sauté aux yeux. Par exemple, dans la méthode où tu fais le alloc/init, si à  la place de nommer ta variable locale ListX (comme le paramètre de ton autre méthode) tu la nommes disons localListX, cela donnerait :
    -(void)methodeA {<br />&nbsp; NSMutableArray * localListX = [[NSMutableArray alloc] init];<br />&nbsp; ...<br />&nbsp; [self&nbsp; remplissageWithNbpoints:1<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; withListX:localListX withListY:localListY<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; withBX:8 withBY:2 withDX:1 withDY:4];<br />}<br /><br />- (int) remplissageWithNbpoints:(int)nb_points <br />						withListX:(NSMutableArray *)listX <br />						withListY:(NSMutableArray *)listY <br />						withBX:(NSInteger)Bx // point arrivé<br />						withBY:(NSInteger)By // point arrivé<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  withDX:(NSInteger)Dx<br />&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;  withDY:(NSInteger)Dy&nbsp; <br /><br />{<br />&nbsp; NSMutableArray * new_listX = [[NSMutableArray alloc] init]; // Va contenir des NSNumber<br />&nbsp; NSMutableArray * new_listY = [[NSMutableArray alloc] init];<br />&nbsp; ...<br />&nbsp; [listX release];<br />&nbsp; [listY release];<br />&nbsp; ...<br />}
    
    Et je ne vois nulle part dans ce code une variable x donnée qui reçoit un objet alloc/init et qui reçoit plus tard un release. Tu as localListX qui reçoit un alloc/init, tu as new_ListX qui reçoit un alloc/init, tu as listX qui reçoit un release... mais localListX n'a pas de release, new_listX non plus, et listX reçoit un release alors que c'est un paramètre passé à  la méthode (donc en toute logique créé à  l'extéieur de la méthode, donc c'est à  celui qui l'a créé de se charger de son release, qui n'a en aucun cas à  être fait à  l'intérieur de la méthode).
    Bref, du grand n'importe quoi.


    Ce qu'il faut faire c'est toujours raisonner en local pour la gestion mémoire. C'est celui qui alloc ou copy ou retain qui release ou autorelease. Point barre, faut pas chercher plus loin.
    - Donc dans ta méthodeA initiale, tu crées en effet une variable locale localListX (ou listX mais bon en différenciant les noms ça t'évitera de confondre) avec alloc/init sur NSMutableArray, puis appele "remplissageWithNbpoints:...." en passant localListX, mais du coup faut terminer, toujours dans la même méthode, par faire un release sur localListX.
    - D'un autre côté, (et d'une façon non liée puisque par le principe d'encapsulation chacun gère sa mémoire donc tu n'as pas à  te poser la question de comment ta methodeA fonctionne, remplissageWithNbpoints:... doit lui aussi être indépendant côté gestion mémoire), dans remplissageWithNbpoints tu peux créer un NSMutableArray "new_listX" avec alloc/init, faire tes ajouts dedans, appeler récursivement remplissageWithNbpoints... ET faire le "release" sur "new_listX".

    Bien sûr, au lieu de faire directement le "return [self remplissageWithNbpoints:...]", si tu veux pouvoir faire le "release" après cet appel, il faut plutôt faire "int res = [self remplissageWithNbpoints:...]", puis le release, puis "return res". Ou alors faire un autorelease (que du coup tu peux faire avant) plutôt que de faire un release après.


    J'ai plein d'autres remarques comme ça (du genre pour des méthodes recursives, préférer créer une NSAutoreleasePool locale ça fera du bien à  ta mémoire, ou respecter les conventions de nommage (ça fait mal aux yeux de voir un nom de variable commencer avec une majuscule alors que les conventions indiquent que les noms commençant par une majuscule sont des noms de classe et pas d'objets)
  • laudemalaudema Membre
    02:36 modifié #7
    Bonjour,

        Comme je ne sais pas ce qu'est self je ne sais pas si test_caseWithX:zX est une méthode de classe que tu n'aurais pas montrée, ou de super, qui aurait de fortes chances dans ton cas d'être une classe Cocoa (et alors tu aurais du l'appeler sur super), d'ailleurs je ne connais pas de méthode test_caseWithX:zX dans Cocoa (peut être dans UIKit). Peut être est ce là  que tu ouvres un nouveau thread, peut être ailleurs encore mais effectivement je n'ai pas vu dans ton code de quoi lancer un nouveau fil..
          Pour la gestion mémoire Ali a raison (toujours ;), même si tu peux penser que tu release bien l'objet que tu as alloué quand tu rajouteras des méthodes à  ta classe à  la fin tu ne sauras plus qui fait quoi. Et le compilateur non plus qui cherche à  t'aider dans cette tâche.
  • pierre68314pierre68314 Membre
    mai 2011 modifié #8
    Pour laudema :
    test_caseWithX:zX  est une méthode servant juste à  déterminer si les coordonnées que je vais tester appartiennent bien à  ma grille.

    Pour Aligator :
    merci pour tes conseils, j'étais complètement passé à  côté de la notion de SCOPE. Je vais travailler d'avantage dans ce sens.
    Ensuite je vais faire un gros effort pour respecter les conventions de nommage.

    Hey, tu dois t'arracher les cheveux avec moi !! o:)


    J'avais pansé au bout de code que tu proposais, mais je pensais que j'allais libérer mes tableaux avant de pouvoir les passer en paramètre:



    Bien sûr, au lieu de faire directement le "return [self remplissageWithNbpoints:...]", si tu veux pouvoir faire le "release" après cet appel, il faut plutôt faire "int res = [self remplissageWithNbpoints:...]", puis le release, puis "return res".



    Je suis perdu dans ce genre de programmes, trop poussé pour moi. :'(
    mais je pensais que si je faisais ça, alors j'aurais un BAD_ACCESS_truc  :o

    <br />int res = [self remplissageWithNbpoints:...withListX:listX...];<br />[listX release];<br />return res;<br />
    



    Donc l'objet sera détruit qu'une fois la méthode vraiment terminée, donc après le "return". Ca m'ôte un sacré poids.

    ps : je savais que le release décrémente le nombre de compteurs sur un objet, mais je ne savais pas que lorsque le compteur atteint 0 l'objet n'est pas détruit de suite.

  • AliGatorAliGator Membre, Modérateur
    02:36 modifié #9
    Ok bah je crois que si tu en es là  de tes réflexions sur la gestion mémoire à  ne pas comprendre la logique (qui doit être devenue intuitive si tu veux continuer à  coder) ou à  ne pas savoir si en faisant tel truc tu risques d'avoir un EXC_BADACCESS, il faut certainement encore que tu révises les bases et refasse des tutos ou lise de bons bouquins sur le sujet.

    La gestion mémoire en Cocoa est logique. Elle suit des règles simples et claires, expliquées dans la doc Apple dédiée. Il suffit de respecter ces règles (qui sont des plus logiques, même pas alambiquées, faut juste les respecter strictement). Mais si tu ne les maà®trises pas et que tu te poses des questions comme celles de tes posts ou que tu n'a pas bien intégré ce mécanisme comme ce qui transparait dans ton code source, c'est qu'il faut encore t'exercer sur ce sujet, avec des bouquins & co, jusqu'à  ce que ça devienne un automatisme et que tu ne te poses même plus la question.
  • pierre68314pierre68314 Membre
    02:36 modifié #10
    Le message est passé.

    SUJET CLOS
  • AliGatorAliGator Membre, Modérateur
    02:36 modifié #11
    ;) j'espère que tu ne l'as pas mal pris hein :P

    Mais crois moi c'est juste que tu y gagneras vraiment à  prendre le temps de bien maà®triser ce sujet avant d'aller plus loin. Comme je le disais dans un autre sujet, pour construire une bonne maison il faut de bonnes fondations, sinon tôt ou tard tu auras des fuites et des rustines à  mettre dans tous les sens pour éviter que tout s'écroule et ça devient vite l'horreur.
    Prendre le temps de bien maà®triser ce sujet n'est certes sans doute pas le plus passionnant, je comprend que tu sois pressé de faire ton premier vrai programme et faire joujou avec, mais c'est important d'avoir un socle solide si tu veux t'éviter des gros souci sur le long terme. En prenant le temps de mieux maà®triser le sujet de la gestion mémoire, tu y gagneras très vite ensuite sur la suite de ton apprentissage.
Connectez-vous ou Inscrivez-vous pour répondre.