Check if NSString instance is contained in an NSArray

CocoaNsstringNsarray

Cocoa Problem Overview


I have an array with a bunch of strings and I want to check if a certain string is contained in the array. If I use the containsObject: message on the array, I'm getting correct results. Do all NSString objects with the same string point to the same object? Or why is the containsObject: working?

NSArray *stringArray = [NSArray arrayWithObjects:@"1",@"2",@"3",anotherStringValue, nil];
if([stringArray containsObject:@"2"]){
  //DO SOMETHING
}

Cocoa Solutions


Solution 1 - Cocoa

Yes, hard-coded NSStrings (string literals) (that is any @"..." in your source code) are turned into strings that exist indefinitely while your process is running.

However NSArray's containsObject: methods calls isEqual: on its objects, hence even a dynamically created string such as [NSString stringWithFormat:@"%d", 2] would return YES in your sample snippet.
This is because NSString's isEqual: (or more precisely its isEqualToString:) method is implemented to be content aware (vs. comparing pointer identities) and thus returns YES for any pair of strings containing the very same sequence of characters (at time of comparison), no matter how and when they were created.

To check for equal (pointer-)identity you'd have to enumerate your array and compare via

NSString *yourString = @"foo";
BOOL identicalStringFound = NO;
for (NSString *someString in stringArray) {
    if (someString == yourString) {
        identicalStringFound = YES;
        break;
    }
}

(which you most likely wouldn't want, though).

Or in a more convenient fashion:

BOOL identicalStringFound = [stringArray indexOfObjectIdenticalTo:someString] != NSNotFound;

(you most likely wouldn't want this one either).


Summing up:

So the reason you're getting a positive reply from containsObject: is NOT because literal strings share the same constant instance, BUT because containsObject: by convention calls isEqual:, which is content aware.

You might want to read the (short) documentation for isEqual: from the NSObject protocol.

Solution 2 - Cocoa

containsObject: performs a value check, not a pointer check. It uses the isEqual: method defined by NSObject and overridden by other objects for testing. Therefore, if two strings contain the same sequence of characters, they will be considered the same.

The distinction between pointer testing and value testing is very important in some cases. Constant strings defined in source code are combined by the compiler so that they are the same object. However, strings created dynamically are not the same object. Here is an example program which will demonstrate this:

int main(int argc, char **argv) {
    NSAutoreleasePool *p = [NSAutoreleasePool new];
    NSString *constantString = @"1";
    NSString *constantString2 = @"1";
    NSString *dynamicString = [NSString stringWithFormat:@"%i",1];
    NSArray *theArray = [NSArray arrayWithObject:constantString];
    if(constantString == constantString2) NSLog(@"constantString == constantString2");
        else NSLog(@"constantString != constantString2");
    if(constantString == dynamicString) NSLog(@"constantString == dynamicString");
        else NSLog(@"constantString != dynamicString");
    if([constantString isEqual:dynamicString]) NSLog(@"[constantString isEqual:dynamicString] == YES");
        else NSLog(@"[constantString isEqual:dynamicString] == NO");
    NSLog(@"theArray contains:\n\tconstantString: %i\n\tconstantString2: %i\n\tdynamicString: %i",
          [theArray containsObject:constantString],
          [theArray containsObject:constantString2],
          [theArray containsObject:dynamicString]);
}

The output of this program is:

>2011-04-27 17:10:54.686 a.out[41699:903] constantString == constantString2
>2011-04-27 17:10:54.705 a.out[41699:903] constantString != dynamicString
>2011-04-27 17:10:54.706 a.out[41699:903] [constantString isEqual:dynamicString] == YES
>2011-04-27 17:10:54.706 a.out[41699:903] theArray contains:
> constantString: 1
> constantString2: 1
> dynamicString: 1

Solution 3 - Cocoa

You can use containsObject to findout if certain string is exist,

NSArray *stringArray = [NSArray arrayWithObjects:@"1",@"2",@"3",anotherStringValue, nil];

if ( [stringArray containsObject: stringToFind] ) {
    // if found
} else {
    // if not found
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionthe ReverendView Question on Stackoverflow
Solution 1 - CocoaRegexidentView Answer on Stackoverflow
Solution 2 - CocoaughoavgfhwView Answer on Stackoverflow
Solution 3 - CocoaisuruView Answer on Stackoverflow