Comprendre la comparaison NSString

83

Les deux comparaisons suivantes donnent la valeur true:

1)

@"foo" == @"foo";

2)

NSString *myString1 = @"foo";
NSString *myString2 = @"foo";
myString1 == myString2;

Cependant, il y a certainement des moments où deux NSStrings ne peuvent pas être comparés à l'aide de l'opérateur d'égalité et [myString1 isEqualToString:myString2]sont obligatoires à la place. Quelqu'un peut-il nous éclairer à ce sujet?

Yarin
la source

Réponses:

165

La raison pour laquelle cela ==fonctionne est à cause de la comparaison de pointeurs. Lorsque vous définissez une constante à l' NSStringaide @"", le compilateur unifie la référence. Lorsque les mêmes constantes sont définies à d'autres endroits de votre code, elles pointeront toutes vers le même emplacement réel en mémoire.

Lorsque vous comparez des NSStringinstances, vous devez utiliser la isEqualToString:méthode:

NSString *myString1 = @"foo";
NSString *myString2 = @"foo";
NSString *myString3 = [[NSString alloc] initWithString:@"foo"];
NSLog(@"%d", (myString2 == myString3))  //0
NSLog(@"%d", (myString1 == myString2)); //1
NSLog(@"%d", [myString1 isEqualToString:myString2]); //1
NSLog(@"%d", [myString1 isEqualToString:myString3]); //1
[myString3 release];

Éditer:

NSString *myString3 = [[NSString alloc] initWithString:@"foo"]; 
// this is same with @"foo"

initWithString:ne crée plus de nouvelle référence, il vous en faudra initWithFormat,

NSString *myString3 = [[NSString alloc] initWithFormat:@"foo"];
Jacob Relkin
la source
6
La plupart des compilateurs feront également myString3un pointeur vers la constante "foo"comme optimisation, donc généralement, ces trois variables pointeront vers le même emplacement mémoire. Ceci est vrai pour gcc et clang (avec les options par défaut). Essayez de compiler ceci: gist.github.com/578568
mipadi
et comment puis-je faire en sorte qu'une variable NSString se comporte exactement comme @ "..."? la raison pour laquelle je demande est b / c dans mon code en ce moment, la constante @ ".." fonctionne mais elle plante dès que je la remplace par une variable NSString .. voir ici
abbood
2
+1, Juste pour ajouter: isEqual:fait en fait une comparaison de chaîne complète et retourne le même résultat que isEqualToStringparce que la référence de protocole NSObject et la référence de classe NSString spécifient explicitement (respectivement): "Si deux objets sont égaux (par -isEqual:), ils doivent avoir le même valeur de hachage "AND" Si deux objets chaîne sont égaux (comme déterminé par la méthode isEqualToString:), ils doivent avoir la même valeur de hachage. "
Éphémère du
13

L'opérateur d'égalité ==compare uniquement les adresses de pointeur. Lorsque vous créez deux chaînes identiques à l'aide de la @""syntaxe littérale , le compilateur détecte qu'elles sont égales et ne stocke les données qu'une seule fois. Par conséquent, les deux pointeurs pointent vers le même emplacement. Cependant, les chaînes créées par d'autres moyens peuvent contenir des données identiques, tout en étant stockées à différents emplacements de mémoire. Par conséquent, vous devez toujours utiliser isEqual:lors de la comparaison de chaînes.

Notez que isEqual:et isEqualToString:renvoie toujours la même valeur, mais isEqualToString:c'est plus rapide.

David M.
la source
2
Notez également que isEqualToString: provoquera une exception si le paramètre qui lui est passé l'est nil. Donc, s'il y a une chance que vous compariez à une chaîne nulle, vous devriez d'abord faire une vérification nulle ou utiliserisEqual:
Sandy Chapman
10

==compare les emplacements en mémoire. ptr == ptr2s'ils pointent tous les deux vers le même emplacement mémoire. Cela fonctionne avec des constantes de chaîne car le compilateur utilise une chaîne réelle pour des constantes de chaîne identiques. Cela ne fonctionnera pas si vous avez des variables avec le même contenu, car elles pointeront vers des emplacements de mémoire différents; utiliser isEqualToStringdans un tel cas.

mipadi
la source
Pouvez-vous nous éclairer avec un exemple de ce que vous entendez par «cela ne fonctionnera pas si vous avez des variables avec le même contenu»
Logicsaurus Rex
6

Dans Cocoa, les chaînes sont comparées à l'aide de la isEqualToString:méthode de NSString .

La comparaison de pointeurs fonctionne dans votre cas car le compilateur est suffisamment doux pour fusionner les deux chaînes littérales pour pointer vers un objet. Il n'y a aucune garantie que deux chaînes identiques partagent une NSStringinstance.

Nikolai Ruhe
la source
Avez-vous une référence officielle à cela? "Il n'y a aucune garantie que deux chaînes identiques partagent une instance NSString."
Logicsaurus Rex
@ user3055655 Je n'ai pas besoin d'une référence: vous pouvez facilement écrire du code qui crée deux NSStringinstances distinctes avec un contenu identique:[NSMutableString string] != [NSMutableString string]
Nikolai Ruhe
@ user3055655 Si vous voulez dire que ma réclamation n'est pas vraie pour les littéraux de chaîne: essayez les littéraux de deux bundles (comme une application et son bundle de tests).
Nikolai Ruhe
Je voulais juste quelque chose à montrer à mes collègues. Je ne m'attendrais pas à ce que les chaînes mutables soient égales, mais déclarer deux instances de NSString et attribuer une @ "valeur de chaîne" garantit toujours la ==fonctionnalité. Cependant, si vous supprimez une NSString, attribuez une valeur, puis supprimez une autre NSString comme celle-ci, NSString stringWithFormat:vous obtenez en fait deux chaînes différentes qui ==échoueront. Vous avez dit qu'il n'y avait aucune garantie que deux instances NSString (pas NSMutableString) partageraient une instance NSString, et j'ai simplement demandé si vous aviez une preuve de cette réclamation afin que je puisse la partager.
Logicsaurus Rex
@ user3055655 Comme je l'ai dit, essayez des littéraux provenant de lots distincts.
Nikolai Ruhe
3

Un exemple montrant comment la comparaison d'adresses en tant que substitut de la comparaison de chaînes se cassera:

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    NSString *s1 = @"foo";
    NSString *s2 = @"foo";
    NSString *s3 = [[[NSString alloc] initWithString:@"foo"] autorelease];
    NSMutableString *s4 = [NSMutableString stringWithString:@"foobar"];
    [s4 replaceOccurrencesOfString:@"bar"
                        withString:@""
                           options:NSLiteralSearch
                             range:NSMakeRange(0, [s4 length])];

    NSLog(@"s1 = %p\n", s1);
    NSLog(@"s2 = %p\n", s2);
    NSLog(@"s3 = %p\n", s3);
    NSLog(@"s4 = %p\n", s4); // distinct from s1

    NSLog(@"%i", [s1 isEqualToString:s4]); // 1

    [pool release];
SK9
la source
0

Regardez cet exemple:

NSString *myString1 = @"foo";
NSMutableString *myString2 = [[NSMutableString stringWithString:@"fo"] stringByAppendingString: @"o"];

NSLog(@"isEquality: %@", ([myString1 isEqual:myString2]?@"+":@"-")); //YES
NSLog(@"isEqualToStringity: %@", ([myString1 isEqualToString:myString2]?@"+":@"-")); //YES
NSLog(@"==ity: %@", ((myString1 == myString2)?@"+":@"-")); // NO

Ainsi, le compilateur est susceptible d'utiliser la méthode isEqualToString pour traiter isEquals pour les pointeurs de NSString et de déréférencement, bien qu'il ne l'ait pas fait. Et les pointeurs sont différents, comme vous le voyez.

Dmitry Zvorikin
la source
-1
  NSString *str1=[NSString stringWithFormat:@"hello1"];
    NSString *str2=[NSString stringWithFormat:@"hello1"];
    NSString *str3 = [[NSString alloc] initWithString:@"hello1"];




// == compares the pointer but in our example we are taking same string value to different object  using @  so it will point to same address so output will be TRUE condition
    if (str1==str2) {
        NSLog(@"Both String are equal");
    }
    else{
        NSLog(@"Both String not are equal");
    }


    // == compares the pointer but in our example we are taking same string value to different object but we have allocated different string so both object will pount to different address so output will be FALSE condition
    if (str1==str3) {

        NSLog(@"Both String are equal");
    }
    else{
        NSLog(@"Both String not are equal");

    }


  // compare:= compares the values of objects so output will be TRUE condition
    if ([str1 compare:str3]== NSOrderedSame) {
        NSLog(@"Both String are equal");

    }
    else{
        NSLog(@"Both String not are equal");

    }


    // isEqual compares the values of objects so output will be TRUE condition

    if ([str1 isEqual:str2]) {

        NSLog(@"Both String are equal");
    }
    else{
        NSLog(@"Both String not are equal");

    }

    // isEqual compares the values of objects so output will be TRUE condition

    if ([str1 isEqual:str3]) {

        NSLog(@"Both String are equal");
    }
    else{
        NSLog(@"Both String not are equal");

    }


    // isEqualToString compares the values of objects so output will be TRUE condition
    if ([str1 isEqualToString:str2]) {

        NSLog(@"Both String are equal");
    }
    else{
        NSLog(@"Both String not are equal");

    }


    // isEqualToString compares the values of objects so output will be TRUE condition
    if ([str1 isEqualToString:str3]) {

        NSLog(@"Both String are equal");
    }
    else{
        NSLog(@"Both String not are equal");

    }

    // == compares the pointers since we have initialized the same value to first object so the pointer be be same for same value so output will be TRUE condition
    if (str1==@"hello1") {

        NSLog(@"Both String are equal");
    }
    else{
        NSLog(@"Both String not are equal");

    }
Manoj Singhal
la source