Fonction Récursive : mutliplication des threads
pierre68314
Membre
Hello,
j'ai une fonction récursive du genre :
<br /><br />- (int) fonctionRecursiveWithNewTable:(NSMutableArray *) uneTable {<br /><br /> // création d'un nouveau tableau : NouvelleTable...<br /><br /> // On place dans NouvelleTable des objets créés à partir de uneTable<br /><br /> // on libère uneTable avec un release<br /><br /> // Si objectif atteint on sort de la fonction<br /> if ( objectif ) return 1;<br /><br /> // Sinon on repart pour un tour<br /> 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
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Ce n'est pas une fonction, mais une méthode.
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.
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.
Voilà ma méthode pour avis
Belle horreur la gestion mémoire dans ton code !
et ensuite j'appelle la méthode :
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
Je suis dans le cas 1, donc je libère. Qu'est-ce que j'ai manqué?
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 :
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 : 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)
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.
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 !!
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:
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
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.
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.
SUJET CLOS
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.