Розуміння порівняння NSString


83

Обидва наступних порівняння оцінюють як істинні:

1)

@"foo" == @"foo";

2)

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

Однак, безумовно, бувають випадки, коли два NSStrings не можна порівняти за допомогою оператора рівності, а [myString1 isEqualToString:myString2]замість цього потрібно. Хтось може пролити це світло?

Відповіді:


165

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

Порівнюючи 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 у моєму коді зараз константа @ ".." працює, але вона виходить з ладу, як тільки я
замінюю

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]
Nikolai Ruhe

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

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 та dereference, хоча цього не потрібно було робити. І вказівники різні, як бачите.


-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");

    }
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.