Grundlegendes zum NSString-Vergleich

83

Beide folgenden Vergleiche werden als wahr bewertet:

1)

@"foo" == @"foo";

2)

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

Es gibt jedoch definitiv Zeiten, in denen zwei NSStrings nicht mit dem Gleichheitsoperator verglichen werden können und [myString1 isEqualToString:myString2]stattdessen erforderlich sind. Kann jemand etwas Licht ins Dunkel bringen?

Yarin
quelle

Antworten:

165

Der Grund, warum ==funktioniert, ist der Zeigervergleich. Wenn Sie eine Konstante NSStringmit definieren@"" , eindeutigisiert der Compiler die Referenz. Wenn dieselben Konstanten an anderen Stellen in Ihrem Code definiert sind, zeigen sie alle auf dieselbe tatsächliche Position im Speicher.

Beim Vergleichen von NSStringInstanzen sollten Sie folgende isEqualToString:Methode verwenden:

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];

Bearbeiten:

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

initWithString:erstellt keine neue Referenz mehr, die Sie benötigen initWithFormat,

NSString *myString3 = [[NSString alloc] initWithFormat:@"foo"];
Jacob Relkin
quelle
6
Die meisten Compiler geben als Optimierung auch myString3einen Zeiger auf die Konstante "foo", sodass im Allgemeinen alle drei Variablen auf denselben Speicherort verweisen. Dies gilt sowohl für gcc als auch für clang (mit Standardoptionen). Versuchen Sie dies zu kompilieren: gist.github.com/578568
mipadi
und wie kann ich eine NSString-Variable genau wie @ "..." verhalten lassen? Der Grund, den ich frage, ist, dass b / c in meinem Code gerade die Konstante @ ".." funktioniert, aber sie stürzt ab, sobald ich sie durch eine NSString-Variable ersetze. Siehe hier
Abbood
2
+1, Nur zum Hinzufügen: FührtisEqual: tatsächlich einen vollständigen Zeichenfolgenvergleich durch und gibt das gleiche Ergebnis zurück, isEqualToStringda in der NSObject-Protokollreferenz und der NSString-Klassenreferenz explizit angegeben wird: "Wenn zwei Objekte gleich sind -isEqual:, müssen sie dasselbe haben." Hash-Wert "AND" Wenn zwei String-Objekte gleich sind (wie durch die Methode isEqualToString: bestimmt), müssen sie denselben Hash-Wert haben. "
Ephemera
13

Der Gleichheitsoperator ==vergleicht nur Zeigeradressen. Wenn Sie zwei identische Zeichenfolgen mithilfe der Literal- @""Syntax erstellen, erkennt der Compiler, dass sie gleich sind, und speichert die Daten nur einmal. Daher zeigen die beiden Zeiger auf dieselbe Stelle. Auf andere Weise erstellte Zeichenfolgen können jedoch identische Daten enthalten, jedoch an unterschiedlichen Speicherorten gespeichert werden. Daher sollten Sie beim Vergleichen von Zeichenfolgen immer verwenden isEqual:.

Beachten Sie, dass isEqual:und isEqualToString:immer den gleichen Wert zurückgeben, aber isEqualToString:schneller ist.

David M.
quelle
2
Beachten Sie außerdem Folgendes isEqualToString: Verursacht eine Ausnahme, wenn der an ihn übergebene Parameter lautet nil. Wenn es also eine Chance gibt, dass Sie mit einer Null-Zeichenfolge vergleichen, sollten Sie entweder zuerst eine Null-Prüfung durchführen oderisEqual:
Sandy Chapman
10

==vergleicht Speicherorte. ptr == ptr2wenn beide auf denselben Speicherort zeigen. Dies funktioniert zufällig mit Zeichenfolgenkonstanten, da der Compiler zufällig eine tatsächliche Zeichenfolge für identische Zeichenfolgenkonstanten verwendet. Es funktioniert nicht , wenn Sie Variablen mit demselben Inhalt haben, da diese auf unterschiedliche Speicherorte verweisen. Verwendung isEqualToStringin einem solchen Fall.

Mipadi
quelle
Können Sie anhand eines Beispiels
erläutern,
6

In Cocoa werden Zeichenfolgen mit der isEqualToString:Methode von NSString verglichen .

Der Zeigervergleich funktioniert in Ihrem Fall, da der Compiler so sanft ist, dass die beiden Zeichenfolgenliterale zusammengeführt werden, um auf ein Objekt zu verweisen. Es gibt keine Garantie dafür, dass zwei identische Zeichenfolgen eine NSStringInstanz gemeinsam nutzen.

Nikolai Ruhe
quelle
Haben Sie einen offiziellen Hinweis darauf? "Es gibt keine Garantie dafür, dass zwei identische Zeichenfolgen eine NSString-Instanz gemeinsam nutzen."
Logicsaurus Rex
@ user3055655 Ich brauche keine Referenz: Sie können einfach Code schreiben, der zwei verschiedene NSStringInstanzen mit identischem Inhalt erstellt:[NSMutableString string] != [NSMutableString string]
Nikolai Ruhe
@ user3055655 Wenn Sie meinen, dass meine Behauptung für String-Literale nicht zutrifft: Probieren Sie Literale aus zwei Bundles aus (wie eine App und ihr Test-Bundle).
Nikolai Ruhe
Ich wollte nur etwas, um Kollegen zu zeigen. Ich würde nicht erwarten, dass veränderbare Zeichenfolgen gleich sind, aber das Deklarieren von zwei Instanzen von NSString und das Zuweisen eines @ "Zeichenfolgenwerts" garantiert immer die ==Funktionalität. Wenn Sie jedoch einen NSString löschen, einen Wert zuweisen und dann einen anderen NSString wie diesen löschen NSString stringWithFormat:, erhalten Sie tatsächlich zwei verschiedene Zeichenfolgen, die ==fehlschlagen. Sie sagten, es gibt keine Garantie dafür, dass zwei NSString-Instanzen (nicht NSMutableString) eine NSString-Instanz gemeinsam nutzen, und ich habe einfach gefragt, ob Sie einen Beweis für diese Behauptung haben, damit ich sie gemeinsam nutzen kann.
Logicsaurus Rex
@ user3055655 Wie gesagt, versuchen Sie Literale aus verschiedenen Bundles.
Nikolai Ruhe
3

Ein Beispiel, das zeigt, wie der Adressvergleich als Ersatz für den Zeichenfolgenvergleich unterbrochen wird:

    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
quelle
0

Schauen Sie sich dieses Beispiel an:

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

Daher verwendet der Compiler wahrscheinlich die isEqualToString-Methode, um isEquals für NSString- und Dereferenzierungszeiger zu verarbeiten, musste dies jedoch nicht. Und die Zeiger sind unterschiedlich, wie Sie sehen.

Dmitry Zvorikin
quelle
-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
quelle