1.8. Allocating and Making Use of Strings

Problem

You want to work with strings in Objective-C

Solution

Use NSString and NSMutableString classes.

Discussion

The NSString and NSMutableString classes allow you to store a string of characters in memory. The NSString class is immutable, meaning that once it is created, its contents cannot be modified. Mutable strings represented with the NSMutableString can be modified once they are created. We will see an example of both of these classes very soon.

Objective-C strings should be placed inside double quotes. The starting double-quote should be prefixed with an at sign (@). For instance, the sentence Hello, World, represented as a string in Objective-C, is written like so:

@"Hello, World"

There are various ways of placing a string inside an instance of NSString or NSMutableString classes. Here is how:

NSString *simpleString = @"This is a simple string";

NSString *anotherString =
  [NSString stringWithString:@"This is another simple string"];

NSString *oneMorestring =
  [[NSString alloc] initWithString:@"One more!"];

NSMutableString *mutableOne =
  [NSMutableString stringWithString:@"Mutable String"];

NSMutableString *anotherMutableOne =
  [[NSMutableString alloc] initWithString:@"A retained one"];

NSMutableString *thirdMutableOne =
  [NSMutableString stringWithString:simpleString];

If you are working with strings, you are probably going to need the length of your string objects from time to time to make specific decisions at runtime. Imagine this scenario: you have asked your user to enter her name in a text field. When she presses the button to confirm her name, you would need to check whether she has in fact entered her name. You can do this by calling the length method on an instance of NSString or any of its subclasses, including NSMutableString, as shown here:

NSString *userName = ...;

if ([userName length] == 0){
  /* The user didn't enter her name */
} else {
  /* The user did in fact enter her name */
}

Another thing that you might want to know about strings is how you can convert a string to its equivalent integral value, i.e., converting a string to an integer, float, or double. You can use the integerValue, floatValue, and doubleValue methods of NSString (or any of its subclasses) to retrieve the integer, float and double values of a string, like so:

NSString *simpleString = @"123.456";

NSInteger integerOfString = [simpleString integerValue];
NSLog(@"integerOfString = %ld", (long)integerOfString);

CGFloat floatOfString = [simpleString floatValue];
NSLog(@"floatOfString = %f", floatOfString);

double doubleOfString = [simpleString doubleValue];
NSLog(@"doubleOfString = %f", doubleOfString);

The output of this code is:

integerOfString = 123
floatOfString = 123.456001
doubleOfString = 123.456000

If you would like to work with C Strings, you can! You will use them like NSString without the leading at sign, like so:

char *cString = "This is a C String";

If you want to convert an NSString to a C String, you must use the UTF8String method of NSString, like so:

const char *cString = [@"Objective-C String" UTF8String];
NSLog(@"cString = %s", cString);

Note

You can use the %s format specifier to print a C String out to the console. In contrast, use the %@ format specifier to print out NSString objects.

To convert a C String to NSString, you must use the stringWithUTF8String: method of the NSString class, as demonstrated here:

NSString *objectString = [NSString stringWithUTF8String:"C String"];
NSLog(@"objectString = %@", objectString);

In order to find a string inside another string, you can use the rangeOfString: method of NSString. The return value of this method is of type NSRange:

typedef struct _NSRange {
  NSUInteger location;
  NSUInteger length;
} NSRange;

If the string that you are looking for (needle) is found inside the target string (haystack), the location member of the NSRange structure will be set to the zero-based index of the first character of needle in haystack. If needle cannot be found in haystack, the location member gets set to NSNotFound. Let’s have a look at an example:

NSString *haystack = @"My Simple String";
NSString *needle = @"Simple";
NSRange  range = [haystack rangeOfString:needle];

if (range.location == NSNotFound){
  /* Could NOT find needle in haystack */
} else {
  /* Found the needle in the haystack */
  NSLog(@"Found %@ in %@ at location %lu",
        needle,
        haystack,
        (unsigned long)range.location);
}

Note

The search done by the rangeOfString: method of NSString class is case-sensitive.

Note

In the Platform Dependencies section of the String Programming Guide published by Apple, it’s been explained why we need to typecast integral values with specifiers such as unsigned long. I highly recommend that you visit the aforementioned guide online.

If you want to have more control over how your search is done on a string, you can use the rangeOfString:options: method, where the options parameter is of type NSStringCompareOptions.

enum {
    NSCaseInsensitiveSearch = 1,
    NSLiteralSearch = 2,
    NSBackwardsSearch = 4,
    NSAnchoredSearch = 8,
    NSNumericSearch = 64,
    NSDiacriticInsensitiveSearch = 128,
    NSWidthInsensitiveSearch = 256,
    NSForcedOrderingSearch = 512,
    NSRegularExpressionSearch = 1024
  };
  typedef NSUInteger NSStringCompareOptions;

As you can see, the values in this enumeration are multiples of 2. That indicates that you can mix them with the logical OR operator (the | pipe character). Let’s say we want to search for a string inside another string but we are not concerned about the case-sensitivity of the search. All we want is to find a string inside another string, whether the case matches or not. Here is how we can do it:

NSString *haystack = @"My Simple String";
NSString *needle = @"simple";
NSRange  range = [haystack rangeOfString:needle
                                 options:NSCaseInsensitiveSearch];

if (range.location == NSNotFound){
  /* Could NOT find needle in haystack */
} else {
  /* Found the needle in the haystack */
  NSLog(@"Found %@ in %@ at location %lu",
        needle,
        haystack,
        (unsigned long)range.location);
}

You can see that we are using the rangeOfString:options: method of NSString with the NSCaseInsensitiveSearch value, which tells the runtime that we want the search to be performed without any regard to case-sensitivity.

Mutable strings are similar to immutable strings. However, they can be modified during runtime. Let’s see an example:

NSMutableString *mutableString =
  [[NSMutableString alloc] initWithString:@"My MacBook"];

/* Add string to the end of this string */
[mutableString appendString:@" Pro"];

/* Remove the "My " string from the string */
[mutableString
 replaceOccurrencesOfString:@"My "
 withString:[NSString string] /* Empty string */
 options:NSCaseInsensitiveSearch /* Case-insensitive */
 range:NSMakeRange(0, [mutableString length])]; /* All to the end */

NSLog(@"mutableString = %@", mutableString);

When the mutableString string gets printed to the console, you will see this:

mutableString = MacBook Pro

You can see that we started with the string "My MacBook" and then removed the "My " string from that original string. So now we have "MacBook". After this, we appended the string " Pro" to the end of this string to get the final value, which is "MacBook Pro".

Get iOS 6 Programming Cookbook now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.