[Résolu] Utilisation des catégories...
En plein ménage dans mon code, je me rend compte que j'avais dupliqué une méthode dont j'avais besoin dans deux classes différentes !
En cherchant un peu, j'ai réalisé que cette méthode pouvait être déclarée dans une catégorie sur NSString.
J'ai fait ceci, dans NSString+MyStringCategory.h :
//
#import <Foundation/Foundation.h>
@interface NSString (MyStringCategory)
-(BOOL) wordsExist:(NSArray *)listOfWords;
@end
et dans NSString+MyStringCategory.m :
#import "NSString+MyStringCategory.h"
@implementation NSString (MyStringCategory)
-(BOOL) wordsExist:(NSArray *)listOfWords {
// Teste si l'un des mots du NSArray listOfWords est présent dans la string
BOOL boolToReturn=NO;
for (int ii=0; ii<[listOfWords count]; ii++) {
boolToReturn=boolToReturn || ([self rangeOfString:[listOfWords objectAtIndex:ii]].location!=NSNotFound);
}
return boolToReturn;
}
J'ai testé, çà fonctionne très bien, et j'ai un peu allégé mon code. Maintenant, j'ai une autre méthode, dont j'aurais également besoin dans plusieurs classes différentes, et qui traite également des NSString, et je me demande si je peux aussi la mettre dans une catégorie... ou s'il y a une autre solution plus adaptée.
Il s'agit d'une méthode qui extrait le premier mot d'une NSString, et qui le retire de la dite NSString. Pour pouvoir modifier la string, j'ai donc passé son adresse.
-(NSString *) extractWord:(NSString **)line;
si line vaut @Ceci est une chaine de caractères, la méthode va retourner @Ceci, et line deviendra @est une chaine de caractères.
Je me demande donc si l'utilisation d'une catégorie est adaptée dans ce cas. Merci d'avance pour vos lumières.
Réponses
Ce serait plus logique que ce soit une catégorie de NSMutableString
+1 pour le NSMutableString.
Si tu permets une suggestion, vu que tu es dans la révision de ton code, j'aurai imaginé dans ta methode wordsExist: soit une boucle du style :
ou alors carrément :
Ca peut valoir le coup selon la taille de ton tableau.
Evidemment...
Merci, effectivement je vais utiliser le première solution, mes NSArray ne contiennent pas des grandes quantités d'items (tout au plus 5 ou 6), çà fait plus propre. Je garde quand même la deuxième solution au cas où...
Concernant ma méthode à mettre en catégorie de NSMutableString, je me posais surtout la question de savoir si je pourrai faire :
ou myFirstWord sera un NSString et le contenu de myMutableString sera modifié, ou alors devrais-je appeler la méthode en passant l'adresse de myMutableString?
Ou encore, ai-je le droit de faire, dans la catégorie :
index étant le NSRange du premier séparateur de mot (espace ou tiret).
Merci pour les conseils
Une catégorie sur NSScanner ?
est tout à fait légitime.
Deux recommandations :
- indiquer dans la documentation que le destinataire du message est modifié ;
- appeler la méthode extractFirstWord.
Pas mal comme solution... Je n'utilisais pas NSScanner jusqu'à présent. Je vais me pencher dessus !
Merci à tous pour votre aide.
Oui ça me parait parfait ça.
Pas besoin de passer l'adresse puisque c'est une catégorie sur NSMutableString tu auras la chaà®ne en question dans "self", comme tu l'as compris vu tes exemples d'ailleurs. Donc pourquoi se compliquer la vie et vouloir passer son adresse alors que tu l'as déjà ?
Je te déconseille fortement de réaffecter "self" en dehors des méthodes d'init.
Tu fais justement une méthode sur NSMutableString donc tu vas pouvoir directement modifier le contenu de self (et pas le réaffecter)
Super...
Sur le principe, c'est exactement ce que j'imaginais !
Le seul détail, c'est que mes mots peuvent être séparés soit pas des espaces (ou sauts de ligne, avec parfois plusieurs espaces !), soit par des tirets (-), soit par les deux
Je dois pouvoir adapter le NSCharacterSet en y ajoutant le tiret, mais alors que se passera-t-il si j'ai une chaine du style :
mot1 mot2 - mot3 mot4.... ?
Merci
Pour les tirets, tu peux créer un NSMutableCharacterSet à base de whitespaceAndNewlineCharacterSet et lui ajouter ensuite le tiret.
(Ca vaut sans doute le coup de ne le construire que la première fois et le garder de côté, dans une variable static, plutôt que le construire à chaque fois ?)
Pour les cas où ta chaà®ne contient plusieurs espaces et/ou tirets à la suite, il suffit dans la méthode popFirstWord d'encapsuler tout le code dans une boucle "while( foundWord.length<=0 && self.length>0)" pour que tant qu'il trouve un premier mot "vide" (parce que 2 espaces à la suite), il recommence, jusqu'à en trouver un de longueur minimum 1... ou que la chaà®ne soit vide et qu'il ne trouve plus aucun mot.
Génial, merci infiniment
Je testerai çà ce soir
Oui d'ailleurs je te laisse adapter et tester, j'ai pas testé le code en vrai hein
Pas de problème... Tu en as déjà fait beaucoup quand même. Je ne vais tout de même pas te demander de tester et de l'intégrer dans mon projet en plus !!!
Désolée pour le délai mais j'ai eu peu de temps à passer sur Xcode ces temps ci et en plus, les modifications étaient assez lourdes car les méthodes à remplacer sont assez souvent utilisées !
Voila ce que j'ai finalement fait et qui fonctionne correctement ; en fait j'ai créé deux méthodes similaires car j'avais aussi besoin d'une méthode qui extrait les lignes :
.h
.m
Mon problème se situe maintenant au niveau du CharacterSet. Pour l'instant je le créée de la façon suivante :
Mais vu que j'utilise assez souvent cette méthode, cela revient à créer une instance à chaque passage, c'est bien çà ? Donc une empreinte mémoire qui risque d'augmenter ?
Ali m'avait conseillé d'utilisé une variable static, créée à partir d'un NSMutableCharacterSet, et je me demande pourquoi "Mutable". Ne puis-je pas définir ma variable static à partir de "charset" ?
Merci d'avance
Non pas de problème.
static NSCharacterSet* sFirstWordCharacterSet;
En fait le problème c'est pour l'initialiser, normalement on peut utiliser initialize, mais dans une catégorie ...
Sinon, il faudra que tu testes au début de ta méthode pour le créer si la variable static est nil.
Ok merci, je étudier çà d'un peu plus près demain...
Un truc comme ça peut être ? En plus ça permet d'avoir quelque chose de flexible, d'éviter de dupliquer le code et de garder qu'une seule instance du characterSet.
@JegnuX : Génial ton truc, çà fonctionne très bien.
Merci infiniment à tous pour votre aide.
Le code final :
De rien
Par contre en relisant j'ai l'impression qu'il manque un autorelease sur le substring/mutableCopy.
Et tant qu'à faire mettre aussi NSMutableString en type de retour de extractFirstWord
Et aussi, -extractCharacterInString: correspond pas trop. J'aurais du mettre un truc comme -extractCharactersSeparatedByCharactersInString: (plus long mais qui indique mieux ce que ça fait)
Bon j'arrête de chipoter. ^^'
Je le trouve bizarre ce code et surtout l'utilisation du static...
Tu n'initialises ton NSMutableCharacterSet qu'une seule fois... mais ce même NSMCS tu le modifies selon les paramètres passés à la méthode. Ce qui fait que la première fois que tu appelles la méthode, ton NSMCS est nil et il est du coup créé, puis tu en retires tous les caractères présents dans ta chaà®ne string et rajoutes tous les caractères présents dans le paramètre.
Au second appel, la NSMCS n'est plus nil... mais elle a déjà été modifiée par le premier appel. Du coup les appels successifs à ta méthode dépendent des appels précédents. Ta méthode n'est plus déterministe (comportement déterministe = pour une entrée donnée tu as toujours la même sortie en résultat, ce n'est donc pas le cas ici car ça dépend des appels précédents) et en plus tu n'as aucun moyen de la remettre à zéro.
Donc si tu crées une NSMutableString "toto tata titi", appelle ta méthode extractFirstWord (qui va créer ton NSMCS et le modifier dès le premier appel) une voire 2 fois... puis fait un [mutStr setString:@toto tata toto] pour réinitialiser/changer le contenu de ta NSMutableString et que tu rappelles extractFirstWord dessus... ton NSMCS ne sera pas réinitialisé, sera encore dans l'état dans lequel il a été laissé après les 2 appels à extractFirstWord précédents, et extractFirstWord risque de ne pas retourner la même chose que la première fois où tu avais "toto tata titi" dans ta chaà®ne...
Bref, pas très logique tout ça...
Je vois pas bien le soucis ? Je réinitialise bien mon NSMCS à chaque passage. C'est juste que je ne fait pas un nouvel alloc/init
Si tu regardes bien :
Par contre oui on pourrait optimiser comme ceci :
Ah ouais en fait j'avais pas compris la logique bizarre de ta fonction... C'était pas clair à la lecture du code cette utilisation de ta variable string (d'autant que le nom un peu générique n'aide pas ^^). Bon ok j'avais pas trop lu les commentaires, mais en mm temps normalement y'a pas besoin d'avoir à les lire :P
Pourquoi mémoriser la chaà®ne juste pour l'utiliser à l'appel précédent pour réinitialiser ? Pourquoi ne pas plutôt reset le NSMCS à la fin pour être totalement stateless ?
Parce que je n'y avait pas pensé en fait >_< Mais oui, c'est bien plus élégant. Pour rappel, j'avais écris l'autre code à 1h du matin et je suis un petit joueur moi, ça fait trop longtemps que j'ai pas fait de nocturne :P
Bon par contre, ptet un warning pour Alf : ce code n'est pas du tout thread-safe à priori, donc fait attention
Oui, honnêtement, entre garder le MCS en static pour ne l'allouer qu'une fois, mais perdre l'aspect thread-safe au passage et devoir le reset à chaque fois... ou devoir faire un "alloc" à chaque appel, franchement l'avantage d'utiliser un static ici est très discutable.
Ca vaudrait le coup si tu avais à le configurer une fois pour toute et ne plus y toucher (si les caractères à utiliser comme séparateurs étaient fixes et pas passés en paramètre au risque d'être différents à chaque fois), mais là vu qu'à chaque appel tu risques de le remodifier ça vaut pas le coup.
Ouais c'est possible... mais là c'est prendre le bulldozer pour tuer une mouche !
Et en plus si tu appelles 500 fois la méthode à chaque fois avec un paramètre différent, tu auras un NSMutableDict de 500 entrées, et donc 500 instances de NSMCS et la mémoire qui va gonfler et ne sera jamais libérée.
Et puis si tu veux vraiment faire un truc comme ça pour vraiment limiter les allocations, n'utilise pas un NSMutableDictionary. Utilise un NSCache. Bien plus efficace surtout que ça se libère tout seul quand il n'y a plus de mémoire (ou quand tu passes l'appli en background, etc), ça permet de soulager ton empreinte mémoire.
Mais bon de toute façon pour si peu je conseille pas forcément cette approche d'avoir une méthode de catégorie et de garder en cache les NSMCS. Je préconise d'avoir un objet splitter dédié, à -la-NSScanner.
Une méthode qu'on doit rappeler plusieurs fois pour boucler mais qui prend un paramètre (les caractères séparateurs), qui doit absolument être toujours le même quand on appelle en boucle pour découper notre chaà®ne sous peine d'avoir des comportements bizarres ou pas cohérents, c'est pas logique. Faut mieux initialiser un objet qui prend ce paramètre des caractères séparateurs et le stocke, et propose une autre méthode, elle à appeler à chaque itération.
Désolée, je ne peux pas tester vos solutions pour l'instant, je suis en vacances dans un trou paumé... et juste l'iPhone pour répondre. Merci pour toutes ces informations
Bon, çà y est, je suis de retour... et j'ai bien étudié vos remarques.
Le fait que la solution ne soit pas thread-safe n'est pas gênante dans mon projet actuel, mais comme je souhaite pouvoir éventuellement réutiliser la catégorie, je vais tout de même éviter !
C'est bien un NSString dont j'ai besoin pour le retour de extractFirstWord, car je n'ai plus de traitement à faire sur le résultat, alors que lorsque j'extrais une ligne, il es possible que j'aie besoin de la découper...
Voici mon code final de la catégorie, qui a l'air de fonctionner correctement. Qu'en pensez vous ?
En particulier, j'ai dû faire un retain sur mes deux statics, et je me demande quand je dois les libérer... Probablement à la fin de l'application, mais est-ce possible ? çà me gêne un peu, mais je ne vois pas comment faire autrement !
Merci encore pour votre aide...
Pourquoi ? Tu penses qu'ils sont où tes statics une fois que ton appli a quittée
;D T'as raison, j'étais pas claire quand j'ai écrit çà . En fait, Je ne me suis pas pas très bien exprimée. Ce qui me gênait, c'est d'avoir des données qui restent toute la vie de l'application, mais en fait, je n'ai pas le choix, car c'est ce qu'il me faut !