Strings in Cocoa, Part 2
Pages: 1, 2
|
|
Mutable strings
Previously, in our discussion of NSString, I mentioned the fact that fundamentally strings in Cocoa are immutable arrays of Unicode characters. This means that once we create a string, we're stuck with it and there's nothing we can do about it with NSString anyway. NSMutableString is our solution. NSMutableString is a subclass of NSString that allows us to do exactly what we couldn't do before -- we can now create strings that posses the ability to modify and edit, or mutate, their contents.
Because NSMutableString is a subclass of NSString, everything we learned about strings in the previous column and this one holds true for mutable strings as well. In this section, I want to walk you through all of the methods that NSMutableString adds to NSString that allow us to edit the contents of mutable strings (I will refer to instances of NSString simply as strings, and instances of NSMutableString as mutable strings).
We have two methods that allow us to append a string to the end of an existing mutable string: They are - appendString: and - appendFormat:, and they work in the following way.
NSMutableString *aMutableString = [[NSMutableString alloc] initWithString:@"Hello"];
[aMutableString appendString:@", World!"];
The appendString message to aMutableString tells aMutableString to add the argument of the message to the end, so the resulting value of aMutableString is "Hello, World!". There are two things here to notice. First, we cannot use the @"..." construct to directly create mutable strings. Remember that this construct created strings that are compiled and are always present. Obviously we cannot change a pre-compiled string, so we have to create a new NSMutableString object like any other object, using alloc and an init method. We can however, use any of the init... methods declared in NSString, because that is the parent class of NSMutableString.
Additionally, note that none of the content-editing methods of NSMutableString return a value, their return type is void. These methods actually do change the value of the receiver string, rather than returning a modified copy of it, as we've become accustomed to with NSString methods.
We can also append a formatted string to the end of an existing string using -appendFormat:. This works exactly the same as the previous method, except you can format the string according to the rules we learned in the last column.
The method - deleteCharactersInRange: accepts a range (NSRange data type) as the sole argument and removes from the receiver string the characters that fall in that range. To make a range, the Foundation Framework provides the function (not a method of any class, a true C-type function) NSMakeRange(unsigned int location, unsigned int length), which takes two arguments that are the components of a range, and returns an NSRange variable. The first argument is the index of the first letter (think arrays -- counting starts from 0) and the second argument is the length of the range, inclusive of the first character. We could then remove the string we added in the previous example so we are left with the original string, "Hello":
NSRange aRange = NSMakeRange(5, 8);
[aMutableString deleteCharactersInRange:aRange];
Moving along with the methods of NSMutableString, we have - insertString: atIndex: which inserts the string given in the first argument into an index of the receiver given in the second argument. The characters in the receiver string that are at specified index and beyond all get displaced toward the end to make room for the inserted string. We would use this method like this:
NSMutableString *aMutableString = [[NSMutableString alloc] initWithString:@"Hello, World!";
[aMutableString insertString:@" (not goodbye)" atIndex:5];
which would transform aMutableString into "Hello (not goodbye), World!". Note that the first character of the word we are inserting is a space. Remember that arrays are indexed starting at index 0, so the "H" in aMutableString is at index 0.
If we want to completely change a string from one value to another, we can use the -setString: method. So we can take out the resulting string, aMutableString, from above and change it to whatever using this method:
[aMutableString setString:@"whatever."];
which makes aMutableString now hold the value "whatever.". This method should remind you of the set... methods we learned about in NSControl.
The final method we have in our mutable string toolbox is - replaceCharactersInRange: withString:, which does exactly what the method name says it will do. The first argument is a range indicating the characters you want to remove from the receiver string, and the second argument is a string which you want to put in their place -- just a straightforward replacement operation. Let's see how this works in code:
NSMutableString *aString = [[NSMutableString alloc] initWithString:@"Hello, World!";];
[aString replaceCharactersInRange:NSMakeRange(7,5) withString:@"Universe"];
And our original string "Hello, World" is changed to "Hello, Universe!".
String miscellany
Before the end of this column, I want to go over a few features of NSString that allow you to modify the case of a string in addition to obtaining numeric values from a string.
In the article about the color meter, we talked about the ability to take numerical values from an interface control by sending the object any of the following messages: intValue, doubleValue, and floatValue. The same is true for strings. That is, NSString responds to these exact messages in the same way as we learned for the interface controls (think polymorphism). The catch is that the string object can only be a number for these methods to work. We can't use these methods to pick a number out of a string. We can, however, find a number in a string, and then return its C-typed value. Suppose we have a string which has some text, and then a number after the text filling the remainder of the string. We could get it out in the following way:
NSString *textAndNumbers = @"Number of eggs in a dozen= 12";
NSString *numberPart = [textAndNumbers substringFromIndex:27];
int numberInADozen = [numberPart intValue];
I admit, this example is a bit contrived, but I imagine you get the idea here. Remember, we have to use the correct ...Value method according to the type of number in the string, and the destination data type.
One last tidbit of NSString madness I want to share with you are the methods that let us change the case of the text in the string: -capitalizedString, -lowercaseString, and - uppercaseString. -lowercaseString returns a string where every character of the receiver is in lowercase, -uppercaseString does the same except every character is converted to uppercase. The last of these three, -capitalizedString, is a little fancier, returning a string where the first letter of each word in the receiver string is converted to its uppercase counterpart. Here are these methods in action:
NSString *string = @"tHe uniVERSity of TEXAS";
NSString *lcstring = [string lowercaseString];
NSString *ucstring = [string uppercaseString];
NSString *capstring = [string capitalizedString];
These last three lines would convert string to "the university of texas", "THE UNIVERSITY OF TEXAS", and "The University Of Texas", in that order.
Final thoughts
In this column we just scratched the surface of Cocoa's string-handling ability. I've mentioned throughout the column that you should check out the NSString class reference (I hope you have the Foundation and Application Kit class reference pages bookmarked by now!) to get more detailed information about the other methods not discussed here. What I wanted to show you here are the basic ways of working with strings, giving you the confidence and the know-how to dig deeper into the dustier, more advanced parts of the NSString class. In the next column we will set ourselves to the task of learning how to develop good memory management habits.
Michael Beam is a software engineer in the energy industry specializing in seismic application development on Linux with C++ and Qt. He lives in Houston, Texas with his wife and son.
Read more Programming With Cocoa columns.
Return to the Mac DevCenter.
You must be logged in to the O'Reilly Network to post a talkback.
Showing messages 1 through 7 of 7.
-
escape characters?
2003-05-13 11:36:11 anonymous2 [Reply | View]
It would be nice to know how to enter returns, tabs, etc as part of NSStrings, since this object does not seem to appreciate entry of printf-style tokens... (newbie struggles)
-
strings in path of file with or without '/'...same?
2002-12-19 03:04:11 anonymous2 [Reply | View]
hi mike,
i have just started studying cocoa programming through your columns. i've got questions below, i'm sorry if the answers might be so obvious as i still am a beginner.
1)
From your column
"
NSString *shortPath = @"~/textFile.txt";
NSString *absolutePath = [shortPath stringByExpandingTildeInPath];
new path: /Users/mike/textFile.txt.
the reverse:
NSString *path = [absolutePath stringByAbbreviatingWithTildeInPath];
would set path to: ~/textFile.txt.
"
...this works well. but when i started out with the absolute path and have it converted to a short path using the stringByAbbreviatingWithTildeInPath, it doesn't abbreviate the path, the resulting path is the same. Does that mean that I really have to begin with the short path and then have it converted to the absolute path and then, abbreviate it again? and thus, when my first path is an absolute path, conversion to a short path is not possible? i tried this and it worked that way. why is this so?
2)
from your column
"
NSString *path = @"Users/mike/textFile";
"
- i have observed that there is a difference between using
a) NSString *path = @"Users/mike/textFile"; and
b) NSString *path = @"/Users/mike/textFile";
letter 'a' is strange when such path is used to open the file...letter 'b' is, i think, the right one although it's not use for file opening/writing in this column.
or...will the two work? just that it would depend on some things?
-NSFreshReader
-
NSStrings are mutable, just not by us?
2001-07-20 14:09:04 tophu [Reply | View]
I am confused. The information presented here seems to be in direct contradiction.
On the first page we do this:
NSString *path = @"Users/mike/textFile";
path = [path stringByAppendingPathExtension:@"txt"];
Then, on the second page, Mike says 'we cannot use the @"..." construct to directly create mutable strings. Remember that this construct created strings that are compiled and are always present. Obviously we cannot change a pre-compiled string...'
So, NSStrings can be modified by Cocoa internally, but not modified directly by us?
Or, perhaps, in the above example path is a pointer that points to address A in memory when it is created, and then when the +stringByAppendingPathExtension: method changes path, it is in fact changing the address that path pointer points to in memory to reference a different object. However, when we change an NSMutableString we are not changing the address value in the pointer, just the value of the object at that address.
Can someone clarify this for me? -
NSStrings are mutable, just not by us?
2001-07-20 21:41:05 Michael Beam |
[Reply | View]
You mentioned this bit of code:
NSString *path = @"Users/mike/textFile";
path = [path stringByAppendingPathExtension:@"txt"];
but in this code there is no changing of string objects. What we have is a string object pointed to by path in the first line. In the second line we are not changing that string object we created in the first line in any way. Rather, we are creating a new string object by taking the original and adding another string to the end of it. We're not changing the string, we're creating a new one, and then reassigning the variable path so that it points to the new string object returned by the method in the second line.
So in the last paragraph of your post, you've hit the right explanation of what is going on. And you were right about what happens when we change an NSMutableString.
Perhaps i could have made this example clearer by choosing a new variable name for the new object. Actually, we'll see in the next column how this is an example of letting objects loose, which needlessly takes up memory since there's no way we can send messages to the first string object in the code. This is because our way of accessing that object was through the "path" variable, but after the second line, that variable points to an object other than the first.
Hope that helps!
Mike
-
Class methods.
2001-07-13 22:23:03 canyonrat [Reply | View]
In addition to all the string creation methods mentioned here, there are a bunch of class methods that differ only in that you aren't responsible for releasing the string.
There is even one, [NString string] IIRC, that creates an empty, immutable string. What good is that?







hehe