Zrozumienie porównania NSString

84

Oba poniższe porównania dają wynik prawda:

1)

@"foo" == @"foo";

2)

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

Jednak z pewnością zdarzają się sytuacje, w których NSStringnie można porównać dwóch s przy użyciu operatora równości i [myString1 isEqualToString:myString2]zamiast tego jest wymagane. Czy ktoś może rzucić na to trochę światła?

Yarin
źródło

Odpowiedzi:

166

Powodem, dla którego ==działa, jest porównanie wskaźników. Podczas definiowania stałej NSStringprzy użyciu @""kompilator ujednolica odwołanie. Gdy te same stałe są zdefiniowane w innych miejscach w kodzie, wszystkie będą wskazywać to samo rzeczywiste miejsce w pamięci.

Porównując NSStringinstancje, należy skorzystać z isEqualToString:metody:

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

Edytować:

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

initWithString:nie tworzy już nowego odniesienia, będziesz potrzebować initWithFormat,

NSString *myString3 = [[NSString alloc] initWithFormat:@"foo"];
Jacob Relkin
źródło
6
Większość kompilatorów stworzy również myString3wskaźnik do stałej "foo"jako optymalizację, więc ogólnie wszystkie trzy zmienne będą wskazywać na tę samą lokalizację w pamięci. Dotyczy to zarówno gcc, jak i clang (z domyślnymi opcjami). Spróbuj skompilować to: gist.github.com/578568
mipadi
jak więc sprawić, by zmienna NSString zachowywała się dokładnie tak, jak @ "..."? powodem, dla którego pytam, jest b / c w moim kodzie, stała @ ".." działa, ale ulega awarii, gdy tylko
zastąpię
2
+1, Wystarczy dodać: isEqual:faktycznie wykonuje pełne porównanie ciągów i zwraca ten sam wynik, isEqualToStringponieważ odniesienie do protokołu NSObject i odniesienie do klasy NSString wyraźnie określają (odpowiednio): "Jeśli dwa obiekty są równe (o -isEqual:), muszą mieć to samo wartość skrótu „AND” Jeśli dwa obiekty typu string są równe (zgodnie z metodą isEqualToString:), muszą mieć tę samą wartość skrótu ”.
Ephemera
13

Operator równości ==porównuje tylko adresy wskaźników. Gdy utworzysz dwa identyczne ciągi przy użyciu @""składni literału , kompilator wykryje, że są one równe i zapisze dane tylko raz. Stąd te dwa wskaźniki wskazują na tę samą lokalizację. Jednak łańcuchy utworzone w inny sposób mogą zawierać identyczne dane, ale być przechowywane w różnych lokalizacjach pamięci. Dlatego należy zawsze używać isEqual:podczas porównywania ciągów.

Zwróć uwagę isEqual:i isEqualToString:zawsze zwracaj tę samą wartość, ale isEqualToString:jest szybsza.

David M.
źródło
2
Zauważ również, że isEqualToString: spowoduje wyjątek, jeśli przekazany mu parametr to nil. Jeśli więc istnieje szansa, jesteś porównując do zera ciąg, należy albo zrobić nil pierwszy czek lub wykorzystanieisEqual:
Sandy Chapman
10

==porównuje lokalizacje w pamięci. ptr == ptr2jeśli oba wskazują na to samo miejsce w pamięci. Dzieje się tak ze stałymi łańcuchowymi, ponieważ kompilator używa jednego rzeczywistego ciągu dla identycznych stałych łańcuchowych. To nie będzie działać, jeśli masz zmienne o tych samych treści, ponieważ będą one wskazywać na różnych miejscach pamięci; używać isEqualToStringw takim przypadku.

mipadi
źródło
Czy możesz oświecić na przykładzie tego, co masz na myśli, „nie zadziała, jeśli masz zmienne o tej samej zawartości”
Logicsaurus Rex
6

W kakao struny są porównywane za pomocą isEqualToString:metody NSString .

Porównanie wskaźników działa w twoim przypadku, ponieważ kompilator jest wystarczająco delikatny, aby połączyć dwa literały ciągów, aby wskazywały na jeden obiekt. Nie ma gwarancji, że dwa identyczne ciągi współużytkują jedną NSStringinstancję.

Nikolai Ruhe
źródło
Czy masz jakieś oficjalne odniesienie do tego? „Nie ma gwarancji, że dwa identyczne łańcuchy współużytkują jedną instancję NSString”.
Logicsaurus Rex
@ user3055655 Nie potrzebuję referencji: możesz łatwo napisać kod, który tworzy dwie odrębne NSStringinstancje z identyczną zawartością:[NSMutableString string] != [NSMutableString string]
Nikolai Ruhe
@ user3055655 Jeśli masz na myśli, że moje twierdzenie nie jest prawdziwe dla literałów ciągów: Spróbuj literałów z dwóch pakietów (takich jak aplikacja i jej pakiet testów).
Nikolai Ruhe
Chciałem tylko coś pokazać współpracownikom. Nie spodziewałbym się, że zmienne ciągi są równe, ale zadeklarowanie dwóch instancji NSString i przypisanie jakiejś @ "wartości ciągu" zawsze gwarantuje ==funkcjonalność. Jednakże, jeśli usuniesz jeden NSString, przypiszesz wartość, a następnie usuniesz inny NSString w ten sposób NSString stringWithFormat:, w rzeczywistości otrzymasz dwa różne łańcuchy, które ==będą się nie powieść. Powiedziałeś, że nie ma gwarancji, że dwie instancje NSString (nie NSMutableString) będą współużytkować jedną instancję NSString, a ja po prostu zapytałem, czy masz jakiś dowód na to twierdzenie, abym mógł się nim podzielić.
Logicsaurus Rex
@ user3055655 Jak powiedziałem, wypróbuj literały z różnych pakietów.
Nikolai Ruhe
3

Przykład pokazujący, w jaki sposób porównanie adresów jako surogat porównania ciągów nie działa:

    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
źródło
0

Sprawdź ten przykład:

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

Tak więc kompilator prawdopodobnie użyje metody isEqualToString do przetworzenia isEquals dla wskaźników NSString i wyłuskiwania, chociaż nie musiał tego robić. Jak widać, wskaźniki są różne.

Dmitry Zvorikin
źródło
-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
źródło