Plantage dû peut-être à  un unichar*

GreensourceGreensource Membre
06:56 modifié dans API AppKit #1
Bonsoir! Je me fait une mini appli qui gèrent des chaines de caractères.
Je dois récupérer le milieu d'une NSString, c'est à  dire sans son premier et dernier caractères. Voilà  comment je mis suis pris:
<br />- (NSString*)wordWithoutBounds:(NSString*)theWord<br />{<br />	unichar* buffer;<br />	[theWord getCharacters:buffer range:NSMakeRange(1, [theWord length]-1)];<br />	return [NSString stringWithCharacters:buffer length:[theWord length]-2];<br />}

Le souci c'est que mon appli plante mais pas dans cette fonction. Ca coince quand elle sort d'une IBAction:
<br />- (IBAction)shake:(id)sender<br />{<br />	NSString* inputString = [[input stringValue] retain];<br />	NSString* middleWord = [self wordWithoutBounds:inputString];<br />	NSLog(@&quot;%@&quot;,middleWord);<br />}

C'est quand je sort de cette fonction que j'ai un:“EXC_BAD_ACCESS”

J'ai penser que ça pouvais venir du unichar*, je ne sais pas trop comment l'initialiser...

Réponses

  • GreensourceGreensource Membre
    06:56 modifié #2
    Bon je vous ai encore embêter pour rien. En effet je n'avais pas allouer de mémoire à  unichar*! J'ai un peu honte quand même, pour ma défense j'ai pas fait de C depuis....au moins tout ça et j'avais pas aimé ^^.
    Donc j'ai fait ça:
    unichar* buffer = malloc(sizeof(char));
    


    Mais je veux bien quand même des retours sur ma façon de faire ma fonction, j'ai l'impression qu'il y a plus simple.
    Et aussi, dois-je vérifier que l'allocation c'est bien faite? Sachant que de toutes façons je ne renvoie pas d'erreur.
    Merci
  • AliGatorAliGator Membre, Modérateur
    06:56 modifié #3
    Quelle curieuse idée de passer par un "getXXX", qui récupère les données mémoire dans un buffer (au lieu d'utiliser les méthodes qui retournent un objet en retour)... d'autant qu'il faut alors gérer leur mémoire, faut allouer ce qu'il faut avant, etc, c'est bien moins pratique que les méthodes Cocoa pures utilisant les objets genre NSString !

    Là  par exemple "getCharacters: range:" est sensée prendre en premier paramètre un buffer... déjà  alloué ! Comme dit dans la doc :
    Upon return, contains the characters from the receiver. buffer must be large enough to contain the characters in the range aRange (aRange.length*sizeof(unichar)).
    Donc là  tu as oublié d'allouer la mémoire (avec malloc, la bonne vieille fonction C qu'on aime oublier quand on fait du Cocoa) dans ton buffer... ([EDIT]Bon ben depuis tu as vu, mais ça suffit pas...[/EDIT]) ... et ne pas oublier de la libérer (free)... après avoir construit ta NSString avec, et avant ton return...!!

    Du coup en fait quand tu essayais d'afficher la NSString générée (via ton NSLog), il va lire dans ce buffer initialisé n'importe comment et lire n'importe où dans la mémoire car tu n'as pas initialisé ton "unichar* buffer". D'où le EXC_BAD_ACCESS logique.


    Quand même contraignant, ces méthodes getMachin utilisant les types C et surtout les unichar*, hein ?

    Mais dis moi, pourquoi ne pas utiliser substringWithRange: ??
    - (NSString*)wordWithoutBounds:(NSString*)theWord<br />{<br />	NSRange r = NSMakeRange(1,[theWord length]-1);<br />	// Attention au cas où theWord fait 1 ou 2 caractères seulement !!<br />	return [theWord substringWithRange:r];<br />}
    
  • MalaMala Membre, Modérateur
    06:56 modifié #4

    substringWithRange:

    Returns a string object containing the characters of the receiver that lie within a given range.

    - (NSString *)substringWithRange:(NSRange)aRange

    Parameters

    aRange
    A range. The range must not exceed the bounds of the receiver.

    Important: Raises an NSRangeException if any part of aRange lies beyond the end of the receiver.

    Return Value

    A string object containing the characters of the receiver that lie within aRange.

    Discussion

    This method treats the length of the string as a valid range value that returns an empty string.

    Availability

    Available in Mac OS X v10.0 and later.
    See Also

    – substringFromIndex:
    – substringToIndex:
    Declared In

    NSString.h


    Mais bon, je dis ça mais je sais pas...  ;)

    Par contre, en C allouer une chaà®ne de caractères avec un seul caractère et y mettre bien plus long ça risque de faire de la casse...  :brule:
  • schlumschlum Membre
    06:56 modifié #5
    De toutes les techniques qu'on peut trouver pour extraire une sous-chaà®ne, je crois bien que tu viens d'inventer la plus pourrie  :P
  • GreensourceGreensource Membre
    06:56 modifié #6
    dans 1238973289:

    Mais dis moi, pourquoi ne pas utiliser substringWithRange: ??

    Mais heuuu, je l'avais pas vu  :P
    Je suis deg' je suis passer à  coté comment neuneu. Ca fait 1/4 que je me dit qu'ils abusent et qu'ils auraient quand même pu la faire cette méthode ^^
    Merci bien les gars

    De toutes les techniques qu'on peut trouver pour extraire une sous-chaà®ne, je crois bien que tu viens d'inventer la plus pourrie 

    Merci! Je m'en fait une spécialité  :)
  • MalaMala Membre, Modérateur
    06:56 modifié #7
    Plus sérieusement, tu connais AppKiDo? On le conseille régulièrement sur le forum car c'est un excellent outil pour naviguer dans la doc des classes d'Apple. En plus, il y a une version pour Mac et une version pour iPhone. Ce qui évite de se mélanger entre la doc de l'un ou l'autre.
  • GreensourceGreensource Membre
    06:56 modifié #8
    Oui je l'avais télécharger déjà  mais j'avoue qu'il me semblais plus pratique d'utiliser xCode pour checké la Doc. Mais visiblement c'est pas super ;-)
  • AliGatorAliGator Membre, Modérateur
    06:56 modifié #9
    J'avoue que pour ma part je suis d'un avis mitigé pour AppKiDo.
    A mes débuts, j'avais pris l'habitude de l'utiliser, et je trouvais ça bien...
    Mais j'ai vite réalisé que du coup à  cause de sa possibilité de lister les méthodes de la classe mais aussi de ses superclasses toutes en même temps... je n'intégrais pas au fur et à  mesure de mes fouilles dans cette doc AppKiDo à  quelle classe appartenait réellement telle ou telle méthode.

    Du coup quand on cherche une méthode particulière, oui c'est pratique car il liste toutes les méthodes qu'une classe supporte, en prenant en compte les classes parentes. Mais ça ne forme pas à  Cocoa forcément super, du coup.

    Pour moi, c'est à  utiliser occasionnellement, quand on cherche une méthode spécifique par exemple, mais pas pour autant systématiquement. Rien ne vaut apprendre à  lire dans la doc Apple (qui s'avère plutôt bien foutue en général) comme ça quand on a un problème, on a pris l'habitude de chercher au bon endroit. Et au fur et à  mesure, l'organisation des classes Cocoa commence à  rentrer.

    Donc je ne dénigre pas AppKiDo, mais disons que c'est un peu comme les bindings ou quoi : ça simplifie les choses, ça semble une bonne idée pour les débutants... mais ça a un effet pervers a long terme, donc il est préférable de prendre les bonnes vieilles méthodes à  terme ;)
  • GreensourceGreensource Membre
    06:56 modifié #10
    Raaa mais il replante maintenant! Pourtant j'ai plus du tout de C, que des NSString!
    J'arrive pas à  comprendre pourquoi ce plantage surviens plus tard? C'est impossible à  tracé du coup!
    Pour l'instant je dirais que ça viens de là :
    <br />- (NSString*)invertCharTo:(NSUInteger)firstIndex from:(NSUInteger)secondIndex inString:(NSString*)theWord<br />{<br />	NSString* s1 = [theWord substringWithRange:NSMakeRange(firstIndex,1)];<br />	NSString* s2 = [theWord substringWithRange:NSMakeRange(secondIndex,1)];<br />	NSString* temp = [theWord stringByReplacingCharactersInRange:NSMakeRange(firstIndex,1) withString:s2];<br />	NSString* result = [temp stringByReplacingCharactersInRange:NSMakeRange(secondIndex,1) withString:s1];<br />	return result;<br />}
    

    Quand je vire l'appel de cette fonction ça passe donc ça dois venir d'ici. Au début je faisait des release sur chaque objet mais après je me suis dit qu'il devais être en autorelease donc pas touche. Mais ça plante toujours.
  • schlumschlum Membre
    06:56 modifié #11
    Est-ce que tu as tenu compte du fait que "result" est un objet "autoreleased" ?
    Et que donc si personne ne fait de "retain" dessus, il sera détruit au prochain cycle de runloop...
  • GreensourceGreensource Membre
    avril 2009 modifié #12
    dans 1238976776:

    Est-ce que tu as tenu compte du fait que "result" est un objet "autoreleased" ?
    Et que donc si personne ne fait de "retain" dessus, il sera détruit au prochain cycle de runloop...

    Non en effet je n'avais pas penser à  ça. J'ai modifié l'appel à  cette méthode du coup:
    <br />                [...]<br />                NSString* temp;<br />		temp = [[self invertCharTo:i from:loc inString:result] retain];<br />		[result release];<br />		result = [NSString stringWithString:temp];<br />		[temp release];<br />                [...]
    

    Mais j'ai toujours ce plantage. J'ai passer Clang dessus pourtant et ça semble bon.

    [edit] Bon c'est en effet plein d'erreur de retain et release, j'avais un packet d'erreur de libération de mémoire. J'ai changer tout ça mais pour moi j'ai une fuite de mémoire ici:
    <br />                NSString* temp;<br />		temp = [[self invertCharTo:i from:loc inString:result] retain];<br />		result = [[NSString stringWithString:temp] retain];<br />
    

    Or Clang ne me dit rien, bizarre non?
  • schlumschlum Membre
    06:56 modifié #13
    Le premier "retain" (sur "temp") est superflu... Détruit au prochain cycle de runloop, ça veut pas dire tout de suite  :P

    Ta gestion de la mémoire a l'air d'être un beau bazar !
    si "result" est une i-var, pourquoi ne pas utiliser le setter au lieu d'appeler des release / retain directement dessus...
  • GreensourceGreensource Membre
    avril 2009 modifié #14
    Mouais, je pensais avoir compris la gestion mémoire, Philippe m'avais bien expliquer, mais finalement je suis pas trop sur de moi.
    Bon là  je vais aller au dodo, je vous file là  où j'en suis rendu.
    Pour la petite explication, ce soft prend un texte en entré, il fait ensuite un mélange à  l'intérieur de chaque mot. Il garde uniquement la position de la première et dernière lettre de chaque mot. Finalement on se rend compte qu'un texte traiter de cette façon est encore à  peut près lisible.

    [une partie du message passé dans WordShaker:]
    Maosiu, je pseians avoir cpoirms la gteoisn mméoeir, Pihlpipe mv&#39;iaas bein elqierxpu, mias faeleninmt je sius pas torp sur de mio.o<br />Bn là  je vias aller au dodo, je vuos flie là  où je&#39;n sius rde.un
    


    Bon il reste plein de réglage à  faire, ça plante si on met un trop grand texte etc...
  • schlumschlum Membre
    06:56 modifié #15
    Dommage, il ne corrige pas les infinitifs au lieu de participes ("er" / "é")  :P
    Tu devrais plutôt filer les sources qu'on te dise ce qui n'est pas bien...
  • GreensourceGreensource Membre
    06:56 modifié #16
    dans 1239006772:

    Dommage, il ne corrige pas les infinitifs au lieu de participes ("er" / "é")  :P
    Tu devrais plutôt filer les sources qu'on te dise ce qui n'est pas bien...

    Ahum...oui bon c'est sûr je suis pas le plus doué pour ça  :P
    Voici les sources
  • schlumschlum Membre
    06:56 modifié #17
    Déjà , t'as oublié le cas de mots à  1 lettre (je pense que c'est pour ça que ça plante)  ;)
  • schlumschlum Membre
    06:56 modifié #18
    Ensuite, pour la mémoire, c'est un peu la cata... t'as mis des "retain" partout ! De peur que les trucs disparaissent ?  ???
  • GreensourceGreensource Membre
    avril 2009 modifié #19
    dans 1239015626:

    Ensuite, pour la mémoire, c'est un peu la cata... t'as mis des "retain" partout ! De peur que les trucs disparaissent ?  ???

    Exact! Et aussi parce qu'il était 2h du mat' et que je voulais quelques chose qui tourne avant de dormir ;-)

    En tous cas ça va me faire un autre projet où apprendre la gestion de la mémoire!
    Si je me rappelle bien et si j'ai bien compris, dès que dans la doc c'est écrit quelque chose du genre:
    Returns a new string...
    Et il faut comprendre qu'il y a un autorelease dessus c'est bien ça?
  • NoNo Membre
    06:56 modifié #20
    dans 1239015782:

    En tous cas ça va me faire un autre projet où apprendre la gestion de la mémoire!
    Si je me rappelle bien et si j'ai bien compris, dès que dans la doc c'est écrit quelque chose du genre:
    Returns a new string...
    Et il faut comprendre qu'il y a un autorelease dessus c'est bien ça?


    Nième rappel concernant la gestion mémoire des objets :

    règle 1 : tu créés un objet (par alloc/init, new ou copy) -> c'est à  toi de le détruire (par un release).

    règle 2 : on te donne un objet tout fait (par exemple en résultat d'appel d'une méthode) -> comme tu ne l'as pas créé, alors tu ne dois pas faire de release.

    règle 3 : tu obtiens un objet (par exemple en résultat d'une méthode) que tu n'as pas créé, mais tu veux être certain de le garder longtemps, alors tu fais un retain dessus. Une fois que tu es sûr de ne plus avoir besoin de cet objet, alors tu lui appliques un release.
  • GreensourceGreensource Membre
    06:56 modifié #21
    Mais justement No je pensais avoir compris mais finalement il reste certaine choses pas claire dans ma tête:
    Entre le point 2 et 3 par exemple. Tu dis que l'ont à  pas à  faire de release, donc cela suppose qu'il y a un autorelease de fait dessus n'est-ce pas? Du coup si par exemple je met cet objet retourné dans un tableau, dois-je faire un retain pour pas qu'il disparaisse? Ou plutôt dans mon cas précis, si je le donne à  afficher à  un NSTextField, dois aussi faire un retain?
    La question reviens à : Lors de l'ajout d'un objet via une méthode, un retain est-il fait dans cette méthode. Ici :[anArray addObject:anObject atIndex:anIndex] y aura t'il un retain de fait?

    Je vais essayer de faire du nettoyage pour amélioré tout ça. Merci
  • NoNo Membre
    06:56 modifié #22
    Dès l'instant que tu mets un objet dans un NSArray (tableau), ce dernier va faire un retain dessus, car il va le garder un certain temps.
    Dès que tu le sors de cet objet, ça va faire un release dessus.

    Si l'objet que tu mets dans ce tableau a été créé par toi, et si après la mise en tableau tu en a plus besoin, tu release (mais il ne disparaitra pas grâce au retain interne du tableau).

    J'ai pris l'exemple du tableau, mais cela vaut pour toutes les collections (dictionnaire, set, etc) d'objets.
  • schlumschlum Membre
    06:56 modifié #23
    Je vais aussi t'apprendre un algorithme de shake, parce que le tien est un peu foireux  :P

    * Déjà  pour avoir du hasard de qualité, il faut utiliser "/dev/urandom", puis tu lis dedans d'un coup (nombre de lettres-1)*sizeof(NSUInteger)
    * Ensuite, tu inverses chaque lettre (sauf la dernière) avec une des suivantes ou elle même (et pas avec une au pif)

    Pour faire ça, pour le coup, vaut mieux récupérer le tableau de caractères !
  • GreensourceGreensource Membre
    06:56 modifié #24
    dans 1239017492:

    Je vais aussi t'apprendre un algorithme de shake, parce que le tien est un peu foireux  :P

    * Déjà  pour avoir du hasard de qualité, il faut utiliser "/dev/urandom", puis tu lis dedans d'un coup (nombre de lettres-1)*sizeof(NSUInteger)
    * Ensuite, tu inverses chaque lettre (sauf la dernière) avec une des suivantes ou elle même (et pas avec une au pif)

    Pour faire ça, pour le coup, vaut mieux récupérer le tableau de caractères !

    Pour le random, je ne sais pas comment importer ton truc, faut faire #import "dev/urandom"?

    Pour ce qui est du mélange je pensais déjà  faire ce que tu dis, j'ai pas fait ça?
  • schlumschlum Membre
    avril 2009 modifié #25
    Non, tu l'inverses avec une au hasard qui peut aussi être avant, ce qui fausse le mélange.

    - (NSString*)randomShake:(NSString*)theWord<br />{<br />	NSUInteger lg = [theWord length];<br />	if(lg&lt;=3)<br />		return [[theWord retain] autorelease];<br />	else {<br />		NSMutableData *uniData = [NSMutableData dataWithCapacity:lg*sizeof(unichar)];<br />		unichar *uniDataTabl = (unichar*)[uniData mutableBytes];<br />		[theWord getCharacters:uniDataTabl];<br />		FILE *fp = fopen(&quot;/dev/urandom&quot;,&quot;r&quot;);<br />		if(fp==NULL)<br />			[[NSException exceptionWithName:@&quot;I/O error&quot;<br />									 reason:@&quot;Cannot open &#39;/dev/urandom&#39; file&quot;<br />								&nbsp;  userInfo:nil] raise];<br />		NSMutableData *rData = [NSMutableData dataWithCapacity:(lg-3)*sizeof(NSUInteger)];<br />		NSUInteger *rDataTabl = (NSUInteger*)[rData mutableBytes];<br />		fread(rDataTabl,lg-3,sizeof(NSUInteger),fp);<br />		fclose(fp);<br />		NSUInteger i;<br />		for(i=1;i&lt;=lg-3;++i) {<br />			NSUInteger r = rDataTabl[i-1]%(lg-1-i);<br />			if(r!=0) {<br />				unichar tmp = uniDataTabl[i];<br />				uniDataTabl[i] = uniDataTabl[i+r];<br />				uniDataTabl[i+r] = tmp;<br />			}<br />		}<br />		return [NSString stringWithCharacters:uniDataTabl<br />									&nbsp;  length:lg];<br />	}<br />}
    


    Cette fonction fait tout... Elle mélange le milieu du mot quand il fait plus de 3 lettres.
  • GreensourceGreensource Membre
    06:56 modifié #26
    Ah c'est gentil ça! Merci. Comme je suis pas copain copain pour l'instant avec le C c'est cool, ça m'apprend pas mal de truc.
    J'ai une ou deux questions qui demeure:

    FILE *fp = fopen("/dev/urandom","r");
    ...
    fread(rDataTabl,lg-3,sizeof(NSUInteger),fp);

    Tu ouvres le fichier urandom, il y a quoi dedans? Des nombres aléatoires? Et tu lis les x premiers dont tu as besoin c'est ça? C'est pas sensé être un programme urandom?
  • schlumschlum Membre
    06:56 modifié #27
    C'est un device, un fichier spécial... On peut lire son flux à  l'infini qui est constitué effectivement de bytes aléatoires (constitué entre autre du bruit aléatoire venant du hardware, comme les mouvements de la souris).
  • schlumschlum Membre
    06:56 modifié #28
    NSMutableData *uniData = [NSMutableData dataWithCapacity:lg*sizeof(unichar)];<br />unichar *uniDataTabl = (unichar*)[uniData mutableBytes];
    


    Ce genre d'astuce que j'utilise 2 fois et qui n'est pas forcément claire au premier regard, c'est pour éviter un "malloc", et allouer un buffer de la taille que je veux sans me préoccuper de leaks ou d'exceptions. Un buffer C auto-released en gros.
  • GreensourceGreensource Membre
    06:56 modifié #29
    Ok, pour voir si j'ai bien compris ça évite un:
    unichar* uniDataTabl = malloc(sizeof(unichar));<br />...<br />free(uniDataTabl);
    

    C'est bien ça?

    Bon sinon je crois avoir chassé les problèmes de mémoire, je vous reposte le projet. Je vais aussi faire une annonce là  ou ça va bien, je suis un peu hors sujet ici.
  • schlumschlum Membre
    06:56 modifié #30
    dans 1239023050:

    Ok, pour voir si j'ai bien compris ça évite un:
    unichar* uniDataTabl = malloc(sizeof(unichar));<br />...<br />free(uniDataTabl);
    


    "lg*sizeof(unichar)", pas juste "sizeof(unichar)"... je veux allouer pour tout le mot, pas pour juste un caractère !

    ça remplace ça, mais c'est plus intelligent car le free est géré par l'AutoRelease pool, et sera donc fait dans tous les cas.
Connectez-vous ou Inscrivez-vous pour répondre.