UITexField vide ou pas

al33eral33er Membre
10:40 modifié dans API UIKit #1
Bonjour,

Pour controller la saisie, existe-t-il une instruction pour tester si une zone a été renseignée ?

Merci.

Alexandre.

Réponses

  • AliGatorAliGator Membre, Modérateur
    10:40 modifié #2
    Bah heu [tt]if ([monTextField.text isEqualToString:@";"]) ..[/tt] ?
  • MalaMala Membre, Modérateur
    10:40 modifié #3
    Ou encore:
    <br />if (monTextField.text.length == 0)<br />
    
  • AliGatorAliGator Membre, Modérateur
    mars 2009 modifié #4
    dans 1236210933:

    Ou encore:
    <br />if (monTextField.text.length == 0)<br />
    

    Héhé j'y pensais c'est ce que je voulais proposer au début...

    Mais côté performances il est je pense plus rapide de comparer une chaà®ne à  la chaà®ne vide (@";" qui en plus doit certainement être une constante statique interne à  NSString j'imagine) plutôt que de demander la longueur et la comparer à  zéro, non ?
    Car si la longueur n'est pas zéro mais qu'on a un texte super long, je ne sais pas l'algo qu'il y a derrière (c'est vrai qu'à  mon avis la longueur d'une NSString est stockée dans la NSString ou au moins mise en cache) mais y'a des chances que ce soit plus long que de demander à  comparer avec une chaà®ne vide, opération pour laquelle il saura répondre dès l'analyse du premier caractère (si c'est le caractère de terminaison \0 ou pas)  ;)

    Bon, ok, c'est du pinaillage, mais bon :D

    Si on était en C pur, j'opterais sans hésiter pour [tt]if(0==strcmp(txt,""))[/tt] (voire [tt]if (!txt || !txt[0])[/tt]) plutôt que [tt]if (0==strlen(txt))[/tt]... donc même si les NSStrings sont bien plus optimisées et mieux foutues que les cStrings, j'ai gardé l'habitude  ;D
  • MalaMala Membre, Modérateur
    mars 2009 modifié #5
    dans 1236212898:

    Mais côté performances il est je pense plus rapide de comparer une chaà®ne à  la chaà®ne vide (@";" qui en plus doit certainement être une constante statique interne à  NSString j'imagine) plutôt que de demander la longueur et la comparer à  zéro, non ?

    Non.

    Illustration par l'exemple:
    <br />&nbsp; &nbsp; &nbsp; &nbsp; NSString *maChaine = [NSString stringWithFormat:@&quot;&quot;];<br />	<br />	NSLog(@&quot;log avant isEqualToString&quot;);<br />	unsigned compteur = 0;<br />	for(int i=0; i&lt;100000000; i++)<br />	{<br />		if( [maChaine isEqualToString:@&quot;&quot;] )<br />		{<br />			compteur++;<br />		}<br />	}<br />	NSLog(@&quot;log apres isEqualToString&quot;);<br />	<br />	NSLog(@&quot;log avant length&quot;);<br />	compteur = 0;<br />	for(int i=0; i&lt;100000000; i++)<br />	{<br />		if( [maChaine length] == 0 )<br />		{<br />			compteur++;<br />		}<br />	}<br />&nbsp; &nbsp; &nbsp; &nbsp; NSLog(@&quot;log apres length&quot;);<br />
    


    Résultats en release sur un C2D 2,16Ghz:
    2009-03-05 01:40:28.239 Test[3607:10b] log avant isEqualToString
    2009-03-05 01:40:38.069 Test[3607:10b] log apres isEqualToString
    2009-03-05 01:40:38.070 Test[3607:10b] log avant length
    2009-03-05 01:40:39.067 Test[3607:10b] log apres length

    Soit 9,83s d'exécution pour isEqualToString et 0,997s pour length. On a un facteur 10 entre les deux...  :)

    Maintenant, si je transforme
    <br />NSString *maChaine = [NSString stringWithFormat:@&quot;&quot;];<br />
    

    en
    <br />NSString *maChaine = @&quot;&quot;;<br />
    

    J'obtiens les résultats suivants:
    2009-03-05 01:47:06.305 ObjCParser[3651:10b] log avant isEqualToString
    2009-03-05 01:47:07.802 ObjCParser[3651:10b] log apres isEqualToString
    2009-03-05 01:47:07.803 ObjCParser[3651:10b] log avant length
    2009-03-05 01:47:08.773 ObjCParser[3651:10b] log apres length

    Soit 1.497s et 0.97s. Cela me laisse supposer qu'avant toute chose le isEqualToString fait une comparaison d'adresses avant même de comparer les chaà®nes alors que length se contente de retourner un entier non signé stocké en cache. Donc dans le second cas on limite la casse mais length garde largement l'avantage.


  • AliGatorAliGator Membre, Modérateur
    mars 2009 modifié #6
    Alors j'ai recopié ton bout de code (dans un projet qui me sert de fourre-tout, qui est de type Cocoa Application et pas FoundationTool mais bon) et je l'ai exécuté avec Shark (dans Xcode, menu Run -> Start with Performance Tool -> Shark).

    Il en ressort ceci :
    - Si je demande "Focus symbol" sur ma fonction C [tt]testIsEqualCall()[/tt] qui fait la boucle avec isEqualToString, je vois qu'il appelle CFStringGetCStringPtr (18% du temps utilisé par le process) et CFStringGetLength (13%)
    - Si je demande "Focus symbol" sur ma fonction C [tt]testLengthCall()[/tt] qui fait la boucle sur length, je vois juste un appel à  _CFStringGetLength2 (2%) (fonction privée apparemment vu le "_" au début du nom ?)

    Donc à  priori _CFStringGetLength2 utilisé par la méthode [tt]length[/tt] est soit une version optimisée du CFStringGetLength, soit plus probablement une version utilisant une valeur en cache (ce qui revient au même en soi). Là  où [tt]isEqualToString[/tt] a l'air de faire à  la fois la récupération du pointeur de données C puis de la longueur de cette chaà®ne pour pouvoir ensuite sans doute parcourir cette dernière et la comparer caractère par caractère (on voit un appel à  dyld_stub_memcmp par isEqualToString, dans Shark)
    Et là  du coup c'est un manque d'optimisation de la part d'Apple j'ai bien l'impression... car ils auraient pu utiliser strcmp et se fier au caractère terminateur plutôt que memcmp... ou en tout cas au moins utiliser leur GetLength2 optimisé ici aussi !

    Bref un peu obscur mais intéressant tout ça... comme quoi à  partir d'une question super simple on peut en arriver... à  apprendre à  utiliser les outils de mesures de performances fournis par Apple (que pour ma part j'utilise que très rarement) et à  creuser plus en profondeur sous le capot ;)  :)

    void testLengthCall()<br />{<br />	NSString *maChaine = [NSString stringWithFormat:@&quot;&quot;];<br />	unsigned i, compteur = 0;<br />	<br />	NSLog(@&quot;log avant length&quot;);<br />	for(i=0; i&lt;100000000; i++)<br />	{<br />		if( [maChaine length] == 0 )<br />		{<br />			compteur++;<br />		}<br />	}<br />	NSLog(@&quot;log apres length&quot;);	<br />}<br />void testIsEqualCall()<br />{<br />	NSString *maChaine = [NSString stringWithFormat:@&quot;&quot;];<br />	unsigned i,compteur = 0;<br />	<br />	NSLog(@&quot;log avant isEqualToString&quot;);<br />	for(i=0; i&lt;100000000; i++)<br />	{<br />		if( [maChaine isEqualToString:@&quot;&quot;] )<br />		{<br />			compteur++;<br />		}<br />	}<br />	NSLog(@&quot;log apres isEqualToString&quot;);<br />}<br /><br /><br />int main(int argc, char *argv&#91;])<br />{<br />	testLengthCall();<br />	testIsEqualCall();<br />	return 0;<br />}
    


    PS : J'ai testé aussi en appellant testIsEqualCall avant testLengthCall dans mon main, au cas où il y aurait une histoire de cache qui rentrerait en jeu... mais ça donne pareil ;) Donc je suis pas allé jusqu'à  tester séparément testLengthCall et testIsEqualCall dans 2 compilations et instrumentations distinctes, il est clair que c'est la complexité et non optimisation de isEqualToString comparé à  length qui joue ici !
  • Philippe49Philippe49 Membre
    mars 2009 modifié #7
    dans 1236212898:

    Si on était en C pur, j'opterais sans hésiter pour [tt]if(0==strcmp(txt,""))[/tt] (voire [tt]if (!txt || !txt[0])[/tt]) plutôt que [tt]if (0==strlen(txt))[/tt]... donc même si les NSStrings sont bien plus optimisées et mieux foutues que les cStrings, j'ai gardé l'habitude  ;D

    Je n'ai jamais vérifié, mais pour moi la NSString emporte avec elle sa length, mise à  jour de manière paresseuse. Donc quand on appelle [aString length] on ne fait qu'accéder à  une donnée.
    De même pour un NSArray, [NSArray count] n'est qu'un accès à  une donnée.

    Qu'en pensez-vous ?

    [EDIT] et après lecture de vos posts, on se rejoint !  ;D
  • BaardeBaarde Membre
    10:40 modifié #8
    dans 1236224537:
    Et là  du coup c'est un manque d'optimisation de la part d'Apple j'ai bien l'impression... car ils auraient pu utiliser strcmp et se fier au caractère terminateur plutôt que memcmp... ou en tout cas au moins utiliser leur GetLength2 optimisé ici aussi !

    En fait, en relisant les sources de CoreFoundation (http://www.opensource.apple.com/darwinsource/10.5.6/CF-476.17/CFString.c), on voit qu'Apple n'utilise pas de caractère terminateur. L'objet stocke un buffer qui contient les caractères de la chaà®ne ainsi que la longueur de ce buffer.

    La différence entre CFStringGetLength() et _CFStringGetLength2() c'est que CGStringGetLength() effectue une vérification et s'assure que le pointeur passé est bien une CFString. De plus, comme NSString et CFStringRef sont interchangeables, CFStringGetLength() peut recevoir une instance d'une sous-classe de NSString (qui est une classe abstraite) autre que NSCFString (celle fournie par Apple). CFStringGetLength() exécute donc la macro CF_OBJC_FUNCDISPATCH0() qui se charge de vérifier qu'il n'y a pas une autre implémentation possible.
    _CFStringGetLength2() n'effectue pas toutes ces vérifications car elle opère sur un objet doit elle est déjà  sûre qu'il s'agit d'un NSCFString (_CFStringGetLength2() étant appelée par -[NSCFString length]). Par contre, elle n'est pas utilisable dans isEqualToString: vu qu'on ignore la structure de la chaà®ne passée en argument.
  • MalaMala Membre, Modérateur
    mars 2009 modifié #9
    Je ne pense pas que notre ami al33er pensait en apprendre autant à  partir d'une question anodine!  :)

    Voilà  le genre de discussion qui me plaà®t sur OC ou comment d'une question basique on en arrive à  décortiquer l'arrière du décor!  :fouf):

    Ca c'est du pur objective-cocoa.org!!!
    :p :p :p :p
Connectez-vous ou Inscrivez-vous pour répondre.