Comparer son style de programmation
Philippe49
Membre
C'est mort actuellement sur le site !
Voilà une petite source de discussion pour animer <br />
On connaà®t la méthode de NSStringÂ
- (NSArray *)componentsSeparatedByString:(NSString *)separator
qui décompose une chaà®ne en un tableau de sous-chaà®ne.
Quel est selon vous un bon code pour une catégorie
@interface NSString (componentsSeparatedByCharactersFromString)
-(NSArray *) componentsSeparatedByCharactersFromString:(NSString *) characters;
@end
ou
@interface NSString (componentsSeparatedByCharacters)
-(NSArray *) componentsSeparatedByCharacters:(NSCharacterSet *) characterSet;
@end
=====================================================
Return Value
An NSArray object containing substrings from the receiver that have been divided by separators.
Discussion
The substrings in the array appear in the order they did in the receiver. If the string begins or ends with the separator, the method ignores. For example, this code fragment:
NSString * string = @(Norman, Stanley, Fletcher);
NSArray *listItems = [string componentsSeparatedByCharactersFromString:@(, )];
produces an array { @Norman, @Stanley, @Fletcher }.
If string begins with a comma and space"for example, ", Norman, Stanley, Fletcher""the array has these contents: { @Norman, @Stanley, @Fletcher }
If string has no separators"for example, "Norman""the array contains the string itself, in this case { @Norman }.
Voilà une petite source de discussion pour animer <br />
On connaà®t la méthode de NSStringÂ
- (NSArray *)componentsSeparatedByString:(NSString *)separator
qui décompose une chaà®ne en un tableau de sous-chaà®ne.
Quel est selon vous un bon code pour une catégorie
@interface NSString (componentsSeparatedByCharactersFromString)
-(NSArray *) componentsSeparatedByCharactersFromString:(NSString *) characters;
@end
ou
@interface NSString (componentsSeparatedByCharacters)
-(NSArray *) componentsSeparatedByCharacters:(NSCharacterSet *) characterSet;
@end
=====================================================
Return Value
An NSArray object containing substrings from the receiver that have been divided by separators.
Discussion
The substrings in the array appear in the order they did in the receiver. If the string begins or ends with the separator, the method ignores. For example, this code fragment:
NSString * string = @(Norman, Stanley, Fletcher);
NSArray *listItems = [string componentsSeparatedByCharactersFromString:@(, )];
produces an array { @Norman, @Stanley, @Fletcher }.
If string begins with a comma and space"for example, ", Norman, Stanley, Fletcher""the array has these contents: { @Norman, @Stanley, @Fletcher }
If string has no separators"for example, "Norman""the array contains the string itself, in this case { @Norman }.
Connectez-vous ou Inscrivez-vous pour répondre.
Réponses
Tandis que si tu utilises un NSCharacterSet c'est pour dire que "l'un des caractères de la série sert de séparateur", il n'y a là aucune notion d'ordre, seulement de présence, d'ailleurs, dans une NSString un caractère peut s'y trouver plusieurs fois tandis que dans NSCharacterSet chaque caractère ne s'y trouve qu'une seule fois.
Ouaip, sauf que...
J'ai un fichier texte Windows.
Je veux récupérer un tableau des lignes composant ce texte.
Le séparateur c'est CRLF (soit 2 caractères).
Je fais comment avec mon NSCharecterSet ?
.
Comment feriez-vous le code de la méthode ?
je me lance pour recevoir les coups : Â :)
@implementation NSString (componentsSeparatedByCharactersFromString)
-(NSArray *) componentsSeparatedByCharactersFromString:(NSString *) characters
{
NSScanner * scanner=[NSScanner scannerWithString:self];
NSCharacterSet * charactersToBeSkipped=[NSCharacterSet characterSetWithCharactersInString:characters];
NSMutableArray * array=[NSMutableArray array];
NSString * current;
while( [scanner scanCharactersFromSet:charactersToBeSkipped intoString:¤t],
[scanner scanUpToCharactersFromSet:charactersToBeSkipped intoString:¤t])
{
[array addObject:current];
}
return [[array retain] autorelease];
}
@end
Est-ce votre conception ... avez-vous une autre approche ?
Bah là tu utilises la méthode fournie !
Plutôt que de réinventer 36 roues pour emprunter le même chemin, pourquoi pas sous-classer NSSet pour implanter un NSStringSet ?
.
Déjà tu peux pas mettre de virgule dans la condition d'un while ! (et dans aucune condition d'ailleurs) et tu as plus simple je pense, en utilisant uniquement les méthodes de NSString :
- (NSRange)rangeOfCharacterFromSet:(NSCharacterSet *)aSet options:(unsigned int)mask range:(NSRange)aRange
- (NSString *)substringWithRange:(NSRange)aRange
Quel intérêt ?
doc de NSCharacterSet :
An NSCharacterSet object represents a set of Unicode-compliant characters.
Ben celui de remplir ce topic, comme demandé par Philippe49 !
.
si, c'est sans problème
En C, et donc en objective-C, le résultat d'une expression (A,B,C) vaut le résultat de la dernière expression (la plus à droite) sauf lorsqu'il s'agit d'un eliste d'arguments d'une méthode
;D ;D ;D
C'est moche ! ???
Peut-être pas au niveau de la pensée, au niveau de l'algorithme,
peut-être au niveau de la lecture si on n'en a pas l'habitude.
oui, surtout qu'au niveau rapidité je doute des méthodes objective-C
Sans polémiquer sur la manière de coder (qui est propre à chacun, et aussi pour éviter les "c'est moche"), je me permets juste de te faire remarquer qu'une méthode non directement liée à l'UI devrait faire attention à l'utilisation de l'autorelease pool.
Dans ton exemple, j'encadrerais ton code par une allocation NSautoreleasePool puis par sa destruction, ou alors, j'éviterais l'emploi des méthodes convénientes pour créer les objets.
.
mais le post est en partie pour cela ...
Si je comprends bien, tu ferais plutôt un alloc-init pour l'objet array renvoyé, avec un
return [array autorelease],
Les autres objets de la fonction seraient dans un pool spécifique afin de disparaà®tre le plus vite possible ...
C'est tout à fait cela.
C'est d'autant plus important si tu appelles cette méthode dans une longue boucle par exemple.
.
J'en ai fait deux implémentations en Objective-C, la première, la plus simple, utilisant l'envoie de message normal, et la deuxième, optimisée, qui utilise les pointeurs de méthodes et les imps :
Première implémentation :
Deuxième implémentation :
PS : Après quelques tests, j'ai pu noter ceci à propos des pointeurs de méthodes :
La création d'un pointeur de méthode spécifique (et donc le cast) n'est nécessaire que si le type retourné par la méthode n'est pas un objet. Je pense qu'il est possible d'étendre les possibilités aux pointeurs en général, le cast d'un pointeur (id) à un pointeur d'un autre type (void *, int *, etc.) ne devrait pas poser de problème.
Donc dans le cas d'une méthode retournant un id, un objet définis ou simplement void on peut simplement définir une variable de type IMP :
Dans le cas d'un type void, étant donné qu'on se fiche de la valeur de retour (puisqu'il n'y en a pas) utiliser une fonction qui ne retourne pas le bon type ne pose pas de problème.
Qu'est-ce qui te fait dire que l'utilisation des pointeurs de méthodes optimise ?
Pour moi, mais je ne suis pas un spécialiste, après la compilation et l'optimisation associée, gcc a du faire sa cuisine pour optimiser au mieux selon ses propres critères, ... que l'on ne connaà®t pas vraiment.
Il faut savoir ce qu'il se passe lorsque tu écris un message :
Lorsque tu écris ça :
GCC va remplacer ce message par une fonction C :
En gros c'est ça, bien sûr il convertit directement le @selector() en variable SEL. GCC fera un remplacement de ce type systématiquement. Il ne peut pas faire plus.
Lorsque tu compiles ton application Objective-C, ton code est linké à la bibliothèque libobjc qui contient la définition de objc_msgSend().
Que fait objc_msgSend() ?
Lorsque la fonction est exécutée, elle commence d'abord par vérifier si le message envoyé n'est pas déjà dans un cache, si c'est le cas elle exécute la méthode en lui passant les arguments du message ainsi que l'objet à qui on a envoyé ce message (le fameux self) et le sélecteur de la méthode (le fameux _cmd).
Dans le cas où la méthode n'est pas cachée, donc qu'elle n'a jamais été exécutée, objc_msgSend() va chercher grâce au pointeur de Class "isa" (que tout objet Objective-C a) ainsi que le sélecteur de la méthode, l'implémentation de la méthode à exécuter, elle cherche d'abord dans la classe elle-même, si elle ne trouve pas elle cherche dans les méthodes de la super-classe et ainsi de suite. Si elle trouve la méthode, elle la cache et l'exécute en lui passant les arguments, puis elle renvoie la valeur de retour du message comme si c'était elle qui la renvoyait.
Si la méthode n'est pas trouvée, ni dans la cache ni grâce au pointeur de classe, objc_msgSend() cherche si l'objet est capable d'exécuté le message : -forward::, si c'est le cas, elle passe à cette méthode la liste des arguments, le sélecteur et l'objet. C'est ce système qui permet de "donner une seconde chance d'exécuter un message".
Si la méthode forward:: n'est pas trouvée, alors là elle cherche la méthode error: et si elle-même ne s'y trouve pas, bah c'est objc_msgSend() qui lance l'erreur et quitte le programme.
Donc tu peux voir que l'envoie de message est un mécanisme complexe. Les pointeurs de méthodes que j'utilise en l'occurrence permettent de contourner ce système. Il consiste à faire cette recherche une et une seule fois par message, ensuite pour utiliser l'implémentation je fais comme objc_msgSend(), je passe à ma méthode l'objet sur qui la méthode est exécuté, le sélecteur représentant la méthode et les arguments de la méthode. Cela permet d'éviter d'avoir à chercher dans le cache et/ou dans le pointeur isa la méthode à exécuter.
Il faut remarquer, cependant, que le cache permet de fortement accélérer l'envoie de message, car une fois un message exécuté celui-ci est caché et plus rapidement accessible. Selon Apple après le caching une exécution de méthode est pratiquement aussi rapide qu'un appel de fonction C. Mais ils recommandent tout de même l'utilisation d'un pointeur IMP dans le cas d'un traitement intensif du style boucle.
Voilà , j'espère avoir éclairé ta lanterne.
Oui, j'avais déjà lu ce mécanisme.
Si je le comprends bien, une méthode ayant déjà été appelée est référencée dans un cache afin d'optimiser les appels ultérieurs à cette méthode. Bien. Ce sera le cas pour rangeForCharactersFromSet:Â options: range:
Pourquoi l'optimisation de code n'aurait pas prévu ce cas, qui se reproduit dans quasiment toute méthode ?
Je te livre un exemple d'optimisation que j'ai rencontré récemment :
{
int i,A;
for(i=0;i<UNMILLIARD;i++)
 A=i;
printf("au revoir");
}
==> temps d'exécution = quasi nul
gcc a été capable de voir que la boucle changeait A, mais que A n'était pas utilisé après. Elle a purement et simplement remplacer la boucle par i=UNMILLIARD !
Donc, pour moi, le cas d'optimisation de code dont on parle a de fortes chances d'être prévu
Je vais essayer de comparer les temps d'exécution ...
hum ...
Ce n'est pas possible tout simplement parce que objc_msgSend() (et ses copines) est une fonction déjà écrite et compilée dans la bibliothèque libobjc, donc pour elle les optimisations sont impossibles puisque le code est déjà compilé (surtout qu'elle est écrite en assembleur car la construction des appels de fonctions et compliquées).
Or, GCC lorsqu'il compile le code l ne fait que convertir le message en appel à la fonction objc_msgSend() et tout est dynamique, comme GCC peut-il faire une optimisation à ce niveau ?
à‰tant donné le dynamisme du langage ce n'est pas possible de prévoir ça.
Il te faudra utiliser une méthode beaucoup plus complexe parce que la mienne est trop rapide pour permettre une comparaison saine.
Oui oui ça contourne le système ! En tout cas, ça le contourne dans le cas du message que j'envoie plusieurs fois, bien sûr tu as toujours le methodForSelector: qu'il faut appeler.
et en plus c'est faux >:( ??? ::) ;D ;D ;D :P (le dernier objet risque de ne pas être pris en compte)!!
voilà un code comme tu les aimes
@implementation NSString (componentsSeparatedByCharactersFromString)
-(NSArray *) componentsSeparatedByCharactersFromString:(NSString *) characters
{
NSScanner * scanner=[NSScanner scannerWithString:self];
NSCharacterSet * charactersToBeSkipped=[NSCharacterSet characterSetWithCharactersInString:characters];
NSMutableArray * array=[NSMutableArray array];
NSString * current;
BOOL founded;
do {
[scanner scanCharactersFromSet:charactersToBeSkipped intoString:nil];
founded=[scanner scanUpToCharactersFromSet:charactersToBeSkipped intoString:¤t];
[array addObject:current];
} while(founded);
return [[array retain] autorelease];
}
@end
Ta méthode supprime les espaces qui sont end début de chaà®ne.
Ensuite, ce truc : return [[array retain] autorelease]; sert à rien
Tu peux faire : return array; directement parce que l'objet reçoit l'autorelease directement grâce à la méthode -array et étant donné que tu n'utilises pas d'autorelease pool dans ta méthode, tu insères deux fois le même objet pour une seule utilisation.
Enfin, c'est pas grave parce que c'est un autorelease mais bon...
oui c'était dans la définition de la méthode
Non, la première méthode était juste, car scanUpToCharactersFromSet: intoString: renvoie YES lorsqu'il scanne des caractères , ce qu'il fait en arrivant en bout de chaà®ne.
Par la contre la deuxième avec le booléen, elle, elle coince en bout de chaà®ne ...
c'est sans appel :
3 fois plus de temps pour ma méthode.
Par contre les deux tiennes sont à peu près équivalentes.
Il resterait à tester une fonction C pure
dans l'ordre : la mienne , les deux tiennes
(les différences de résultats s'expliquent puisque l'on n'a pas traité de la même façon les extrémités de la chaà®ne)
le code d'essai :
[Fichier joint supprimé par l'administrateur]
oui
Il me semble : une "convenient method" comme scannerWithString: crée le scanner en autorelease.
Par contre, en forçant le nombre de boucles dans le test, je tombe sur un manque de mémoire qui est sans doute dû au fait que la libération de la mémoire des objets en autorelease sont réalisés en lazy-mode. Cela confirme le post de Bru.
Tout à fait d'accord.
J'avais été surpris de le trouver dans le script Apple "Place Accessor Defs On Clipboard" (menu des scripts de XCode), alors un peu de provoque ...
Merci pour cette discusion