Understanding NSString comparison
Asked Answered
S

7

86

Both the following comparisons evaluate to true:

1)

@"foo" == @"foo";

2)

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

However, there are definitely times where two NSStrings cannot be compared using the equality operator, and [myString1 isEqualToString:myString2] is required instead. Can someone shed some light on this?

Survance answered 13/9, 2010 at 19:6 Comment(0)
A
170

The reason why == works is because of pointer comparison. When you define a constant NSString using @"", the compiler uniquifies the reference. When the same constants are defined in other places in your code, they will all point to the same actual location in memory.

When comparing NSString instances, you should use the isEqualToString: method:

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

Edit:

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

initWithString: does not create a new reference any more, you will need initWithFormat,

NSString *myString3 = [[NSString alloc] initWithFormat:@"foo"];
Abrams answered 13/9, 2010 at 19:11 Comment(3)
Most compilers will also make myString3 a pointer to the constant "foo" as an optimization, so generally, all three of these variables will point to the same memory location. This is true for both gcc and clang (with default options). Try compiling this: gist.github.com/578568Attention
and so how can i make an NSString variable behave exactly like @"..."? the reason I ask is b/c in my code right now the constant @".." works but it crashes as soon as I replace it with an NSString variable.. see hereHellespont
+1, Just to add: isEqual: does in fact do a full string comparison and returns the same result as isEqualToString because the NSObject Protocol Reference and NSString Class Reference explicitly specify (respectively): "If two objects are equal (by -isEqual:) they must have the same hash value" AND "If two string objects are equal (as determined by the isEqualToString: method), they must have the same hash value."Genic
S
13

The equality operator == only compares pointer addresses. When you create two identical strings using the literal @"" syntax, the compiler will detect that they are equal, and only store the data once. Hence, the two pointers point to the same location. However, strings created by other means may contain identical data, yet be stored at different memory locations. Hence, you should always use isEqual: when comparing strings.

Note that isEqual: and isEqualToString: always return the same value, but isEqualToString: is faster.

Settera answered 13/9, 2010 at 19:11 Comment(1)
Also note that isEqualToString: will cause an exception if the parameter passed to it is nil. So if there's a chance you're comparing to a nil string, you should either do a nil check first or use isEqual:Waves
A
10

== compares locations in memory. ptr == ptr2 if they both point to the same memory location. This happens to work with string constants because the compiler happens to use one actual string for identical string constants. It won't work if you have variables with the same contents, because they'll point to different memory locations; use isEqualToString in such a case.

Attention answered 13/9, 2010 at 19:12 Comment(1)
Can you enlighten with an example of what you mean "it wont work if you have variables with the same contents"Leflore
D
6

In Cocoa strings are compared using NSString's isEqualToString: method.

Pointer comparison works in your case because the compiler is gentle enough to merge the two string literals to point to one object. There's no guarantee that two identical strings share one NSString instance.

Donoghue answered 13/9, 2010 at 19:10 Comment(5)
Do you have any official reference to this? "There's no guarantee that two identical strings share one NSString instance."Leflore
@user3055655 I don't need a reference: You can easily write code which creates two distinct NSString instances with identical content: [NSMutableString string] != [NSMutableString string]Donoghue
@user3055655 If you mean that my claim is not true for string literals: Try literals from two bundles (like an app and its tests bundle).Donoghue
I just wanted something to show co-workers. I wouldn't expect mutable strings to be equal, but declaring two instances of NSString and assigning some @"string value" does always guarantee == functionality. However, if you delcare one NSString, assign a value, and then delcare another NSString like this NSString stringWithFormat: then you in fact get two different strings which == will fail on. You said there's no guarantee that two NSString (not NSMutableString) instance will share one NSString instance, and I simply asked if you have any proof of that claim so I could share it.Leflore
@user3055655 As I said, try literals from distinct bundles.Donoghue
T
3

An example demonstrating how address comparison as a surrogate for string comparison will break:

    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];
Tremendous answered 17/1, 2012 at 10:42 Comment(0)
V
0

Check out this example:

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

So, the compiler is likely to use isEqualToString method to process isEquals for NSString's and dereference pointers, though it had not to. And the pointers are different, as you see.

Vestal answered 17/4, 2017 at 17:30 Comment(0)
M
-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");

    }
Malamute answered 5/5, 2017 at 7:46 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.