Понимание сравнения NSString

84

Оба следующих сравнения истинны:

1)

@"foo" == @"foo";

2)

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

Однако бывают случаи, когда два NSStrings нельзя сравнивать с помощью оператора равенства, и [myString1 isEqualToString:myString2]вместо этого требуется. Может кто-нибудь пролить свет на это?

Ярин
источник

Ответы:

166

Причина, по которой ==работает, заключается в сравнении указателей. Когда вы определяете константу NSStringusing @"", компилятор унифицирует ссылку. Когда одни и те же константы определены в других местах вашего кода, все они будут указывать на одно и то же фактическое место в памяти.

При сравнении NSStringэкземпляров следует использовать isEqualToString:метод:

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

Редактировать:

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

initWithString:больше не создает новую ссылку, вам понадобится initWithFormat,

NSString *myString3 = [[NSString alloc] initWithFormat:@"foo"];
Яков Релкин
источник
6
Большинство компиляторов также создают myString3указатель на константу "foo"в целях оптимизации, поэтому обычно все три эти переменные будут указывать на одно и то же место в памяти. Это верно как для gcc, так и для clang (с параметрами по умолчанию). Попробуйте скомпилировать это: gist.github.com/578568
mipadi,
и как я могу заставить переменную NSString вести себя точно так же, как @ "..."? причина, по которой я спрашиваю, это b / c в моем коде прямо сейчас константа @ ".." работает, но она вылетает, как только я заменяю ее переменной NSString .. см. здесь
abbood
2
+1, Просто чтобы добавить: isEqual:действительно выполняет полное сравнение строк и возвращает тот же результат, что и isEqualToStringпотому, что ссылка на протокол NSObject и ссылка на класс NSString явно указывают (соответственно): «Если два объекта равны (по -isEqual:), они должны иметь одинаковые хэш-значение «И» Если два строковых объекта равны (как определено методом isEqualToString:), они должны иметь одинаковое хеш-значение ».
Ephemera
13

Оператор равенства ==сравнивает только адреса указателей. Когда вы создаете две идентичные строки с использованием буквального @""синтаксиса, компилятор обнаружит, что они равны, и сохранит данные только один раз. Следовательно, два указателя указывают на одно и то же место. Однако строки, созданные другими способами, могут содержать идентичные данные, но при этом храниться в разных ячейках памяти. Следовательно, вы всегда должны использоватьisEqual: при сравнении строк.

Обратите внимание, что isEqual:и isEqualToString:всегда возвращает одно и то же значение, но isEqualToString:быстрее.

Дэвид М.
источник
2
Также обратите внимание, что isEqualToString: вызовет исключение, если переданный ему параметр имеет значение nil. Так что, если есть шанс, что вы сравниваете строку с нулем, вам следует либо сначала выполнить проверку на ноль, либо использоватьisEqual:
Сэнди Чепмен
10

==сравнивает места в памяти. ptr == ptr2если они оба указывают на одно и то же место в памяти. Это происходит при работе со строковыми константами, потому что компилятор использует одну фактическую строку для идентичных строковых констант. Это не сработает, если у вас есть переменные с одинаковым содержимым, потому что они будут указывать на разные места в памяти; использовать isEqualToStringв таком случае.

мипади
источник
Можете ли вы пояснить на примере, что вы имеете в виду: «это не сработает, если у вас есть переменные с одинаковым содержанием»
Logicsaurus Rex
6

В Какао строки сравниваются с помощью isEqualToString:метода NSString .

Сравнение указателей работает в вашем случае, потому что компилятор достаточно мягкий, чтобы объединить два строковых литерала, чтобы указать на один объект. Нет гарантии, что две одинаковые строки используют один NSStringэкземпляр.

Николай Рухе
источник
У вас есть официальное упоминание об этом? «Нет гарантии, что две идентичные строки совместно используют один экземпляр NSString».
Logicsaurus Rex
@ user3055655 Мне не нужна ссылка: вы можете легко написать код, который создает два разных NSStringэкземпляра с одинаковым содержанием:[NSMutableString string] != [NSMutableString string]
Николай Рухе,
@ user3055655 Если вы имеете в виду, что мое утверждение неверно для строковых литералов: попробуйте литералы из двух пакетов (например, приложение и его набор тестов).
Николай Рухе
Я просто хотел что-то показать коллегам. Я бы не ожидал, что изменяемые строки будут равны, но объявление двух экземпляров NSString и присвоение некоторого @ "строкового значения" всегда гарантирует ==функциональность. Однако, если вы удалите одну NSString, назначите значение, а затем удалите другую NSString, как это, NSString stringWithFormat:вы фактически получите две разные строки, которые ==не сработают. Вы сказали, что нет никакой гарантии, что два экземпляра NSString (не NSMutableString) будут совместно использовать один экземпляр NSString, и я просто спросил, есть ли у вас какие-либо доказательства этого утверждения, чтобы я мог им поделиться.
Logicsaurus Rex
@ user3055655 Как я уже сказал, попробуйте литералы из разных пакетов.
Николай Рухе
3

Пример, демонстрирующий, как сравнение адресов как суррогат для сравнения строк не работает:

    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
источник
0

Посмотрите этот пример:

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

Таким образом, компилятор, вероятно, будет использовать метод isEqualToString для обработки isEquals для указателей NSString и разыменования, хотя этого и не должно было. И указатели, как видите, разные.

Дмитрий Зворикин
источник
-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");

    }
Манодж Сингхал
источник