Usage of NSString and NSMutableString objects in Objective C - objective-c

I need to use a bunch of string variables throughout my program. I reassign some of them quite often, while others are stuck with the same value during execution.
What's the best practice here?
In the first case, the variables should be NSMutableString and I should cast them to NSString (using the copy method) whenever they need to be arguments of functions that require NSString objects. Is that right?
When I reassign them to other constant values, I shouldn't have to dispose the previous content, right?
As for NSString objects, if I need to assign a new value to them, I guess I should deallocate them, allocate them again, and then assign the new value. Is that correct?

Unless you're actually modifying a string, you shouldn't use NSMutableString. You're reassigning the whole string to a new value, so stay with a regular NSString. Use the autoreleased versions, because that'll be more efficient than alloc/init/release all the time. You could also just reassign your strings to constants if you know what they'll be assigned to.
In the first case, the variables should be NSMutableString and I should cast them to NSString (using the copy method) whenever they need to be arguments of functions that require NSString objects. Is that right?
Well, you could do it that way, but it would be really inefficient. Remember inheritance—an NSMutableString is an NSString, just with some new stuff tacked on. A simple cast will do the trick:
NSString *string = (NSString *)aMutableString;
Even better though, you don't even have to do that. Because of inheritance, you can directly pass in a mutable string wherever a regular string is required, no casting required. That's the beauty of inheritance.
When I reassign them to other constant values, I shouldn't have to dispose the previous content, right
For neither mutable or immutable strings. Old values are simply overwritten in memory—nothing to dispose of there. As far as the memory management goes, it's really not efficient to literally be creating new strings all the time. Just reassign them. You will never need to alloc/init one string more than once, and that single init should be balanced by a single release.
Addendum: When Should You Use Mutable?
A mutable string should be used when you are physically changing the value of the existing string, without completely discarding the old value. Examples might include adding a character to the beginning or the end, or changing a character in the middle. With a mutable string, you can do this "in place"—you'll just modify the existing string. By contrast, an immutable string, once its value is set, cannot change that value. NSString has methods such as stringByAppendingString:, which does add a string to an existing one—but it returns a new string. Behind the scenes, NSString has copied your old string to a new (larger) memory location, added the argument, and returned the new string. That copying is a lot less efficient (relatively speaking, or if you have to do it a lot).
Of course, there's nothing stopping you from physically assigning one string to another. Old values will be overwritten. Most NSStrings, including the #"String Constants", are autoreleased. If you are creating a new string and you decide to alloc/init, you can then assign it to another value without consequence:
myString = anotherString;
myString = myTextField.text;
You can do this with both mutable and immutable strings. The main takeaway is that you should only use mutable when your changing the string itself. But you can change the variable with both mutable and immutable strings without compiler or runtime issues (short of memory management, but most of it is autoreleased anyway).

As for NSString objects, if I need to
assign a new value to them, I guess I
should deallocate them, allocate them
again, and then assign the new value.
Is that correct?
You don't deallocate NSString if you didn't allocated it before, like here:
NSString *string = [NSString stringWithFormat:#"Hello"];
You only need to deallocate it when you call alloc:
NSString *string = [[NSString alloc] initWithString:#"Hello"];
[string release];

The only difference between NSMutableString* and NSString* is that mutable string can be changed.
You don't have to cast anything, since NSMutableString is a subclass of NSString, nor take different memory measures ( so you are right * ).
If you need a modifiable version of a string you just do
NSMutableString* myMutableString = [NSMutableString stringWithString:myString];
You should not 'copy' anything.
Note that if you call :
NSString* myString = myMutableString;
myString is still a mutable String.
So if for any reason (security...) you relly need unmutable strings, you have to call
NSString* myString = [NSString stringWithString:myMutableString];
* you are right, but you could also call [replaceCharactersInRange:withString:] on the mutable string. if there is enough space from previous allocation, then it may be faster, since there is no destruction and new allocation to do.
( Added later : forgot the setString: method )

Related

Is there risk in assigning the output of stringByAppendingString to the same instance?

Is there risk in assigning the output of -[NSString stringByAppendingString] to the same instance? I remember running into issue with this but don't recall the exact situation.
string = [string stringByAppendString:#"more string"];
Before your line executes, string is a pointer to one NSString instance. Afterward, string points to a different instance. You haven't changed the original, you just don't have a pointer to it anymore.
If you're using ARC (the default), or the original string was autoreleased or is a string literal constant, there's no further cleanup to worry about.

iOS memory management about NSString

Is there any different between
NSString * str = #"123";
and
NSString * str = [[NSString alloc] initWithString:#"123"];
from compiler's aspect?
Theoretically yes; in implementation detail, probably not.
In the first case, the compiler creates a constant string and assigns a pointer to it to the variable str. You do not own the string.
In the second case, the compiler creates a constant string (as before) but this time it is used by the run time as a parameter in initialising another string that you do own (because the second string was created using alloc).
That's the end of the stuff you need to know.
However, there is a lot of optimisation that goes on. Because NSStrings are immutable, you'll find that initWithString: actually just returns the parameter. Normally, it would retain the parameter before returning it to you (because you are expecting to own it) but literal strings have a special retainCount (INT_MAX I think) to stop the run time from ever trying to deallocate them. So in practice, your second line of code produces identical results to the first.
This incidentally, is why it is incorrect top say the string is autoreleased in the first case, because it isn't. It's just a constant string with a special retain count.
But you can and should safely ignore the implementation detail and just remember, you don't own the string in the first case, but you do own it in the second case.
Lots of differences. The most important is that you own the second string so you're responsible for releasing it (as is the case whenever you get an object from the init family of methods).
Another is that the former creates a string literal, and if you make a new string with the same literal, they will be pointers to the same object. If you do this:
NSString * str1 = #"123";
NSString * str2 = [[NSString alloc] initWithString:#"123"];
NSString * str3 = #"123";
Then str1 == str2 is false, but str1 == str3 is true. (Of course, the string content is the same, so isEqual: will return true. Also, while this does make for faster comparison, you probably shouldn't use it because it's an implementation detail and could in theory change in the future).
Yes, in the first case you do not own the string and you are not responsible to release it.
In the second case, instead, you are calling alloc thus you become the owner of the object and you must call release on it when you have done, otherwise it will become a memory leak.
In general, if the method you use to get your object contains "new","alloc","copy" or "mutableCopy" then you are the owner of the object and you are responsible to release it.
Check the memory management rules
Yes. The first is assignment of an NSString, and in the second the alloc (which means you need to release it in some way later) and initWithString: method are getting called.
Yes , first statement creates an autorelease object.
Second one creates an object occupying some memory and you have to release it after using it.
The main important difference about memory (your question title) is:
when you do:
NSString* myString = #"my text";
you are allocating an object of NSConstantString type.
The difference with NSString is:
NSConstantString is statically allocate, while NSString is dynamically allocated.

Understanding array pointer

I'm new to Objective-C and need help with the concept of pointers. I've written this code:
//myArray is of type NSMutableArray
NSString *objectFromArray = [myArray objectAtIndex:2];
[objectFromArray uppercaseString];
I assumed that this would change the string at myArray[2] since I got the actual pointer to it. Shouldn't any changes to the dereferenced pointer mean that the object in that location changes? Or does this have something to do with 'string immutability'? Either way, when I use NSLog and iterate through myArray, all the strings are still lowercase.
Shouldn't any changes to the dereferenced pointer mean that the object in that location changes?
Yes, they would. But if you read the documentation for uppercaseString, you see that it does not modify the string in place. Rather, it returns a new uppercase version of the original string. All methods on NSString work like that.
You would need an instance of NSMutableString to be able to modify its contents in place. But NSMutableString does not have a corresponding uppercase method, so you would have to write it yourself (as a category on NSMutableString).
of course!! no string in the array will be converted to uppercase as the statement [objectFromArray uppercaseString]; would have returned the uppercase string which was not collected in any object though. "uppercaseString" does not modify the string object itself with which is is called...!!

Using malloc to allocate an array of NSStrings?

Since NSSstring is not defined in length like integer or double, do I run the risk of problems allocating an array of NSStrings for it using malloc?
thanks
ie:
NSString ***nssName;
nssName = (NSString***) malloc(iN * sizeof(NSString*));
the end result with for_loops for the rows is a 2D array, so it is a little easier to work then NSArray(less code).
No problems should arise, allocating an array of NSStrings is like making an array of the pointers to string objects. Pointers are a constant length. I would recommend just using NSArray but it is still fine to use a C array of NSStrings. Note that this may have changed with ARC.
Here is completely acceptable code demonstarting this:
NSString** array = malloc(sizeof(NSString*) * 10); // Array of 10 strings
array[0] = #"Hello World"; // Put on at index 0
NSLog(#"%#", array[0]); // Log string at index 0
Since NSString is an object (and to be more precise: an object cluster) you cannot know its final size in memory, only Objective-C does. So you need to use the Objective-C allocation methods (like [[NSString alloc] init]), you cannot use malloc.
The problem is further that NSString is an object cluster which means you do not get an instance of NSString but a subclass (that you might not even know and should not care about). For example, very often the real class is NSCFString but once you call some of the methods that treat the string like a path you get an instance of NSPathStore2 or whatever). Think of the NSString init methods as being factories (as in Factory Pattern).
After question edit:
What you really want is:
NSString **nssName;
nssName = (NSString**) malloc(iN * sizeof(NSString*));
And then something like:
nssName[0] = #"My string";
nssName[1] = [[NSString alloc] init];
...
This is perfectly fine since you have an array of pointers and the size of pointer is of course known.
But beware of memory management: first, you should make sure the array is filled with NULLs, e.g. with bzero or using calloc:
bzero(nssName, iN * sizeof(NSString*));
Then, before you free the array you need to release each string in the array (and make sure you do not store autoreleased strings; you will need to retain them first).
All in all, you have a lot more pitfalls here. You can go this route but using an NSArray will be easier to handle.
NSStrings can only be dealt with through pointers, so you'd just be making an array of pointers to NSString. Pointers have a defined length, so it's quite possible. However, an NSArray is usually the better option.
You should alloc/init... the NSString*s or use the class's factory methods. If you need an array of them, try NSArray*.
You should not use malloc to allocate data for Objective-C types. Doing this will allocate memory space but not much else. Most importantly the object will not be initialized, and almost as importantly the retain count for the object will not be set. This is just asking for problems. Is there any reason you do not want to use alloc and init?

NSString setter using isEqualToString

In the Pragmatic Core Data book, I came across this code snippet for an NSString setter:
- (void)setMyString:(NSString*)string;
{
#synchronized(self) {
if ([string isEqualToString:myString]) return;
[myString release];
myString = [string retain];
}
}
Is there any reason to use [string isEqualToString:myString] instead of string == myString here? Does it not mean that if the two strings have the same content, the result will be different than if they are actually the same object? Does this matter?
Thanks.
Notice that the variables you're comparing are pointers to NSStrings. Pointer comparison just checks if the pointers are referring to the same address. It doesn't know anything about the content at the end. Two string objects in two different places can have the same content. Thus you need isEqualToString:. In this case, I'm not sure either that it's a terribly important distinction to make though. It would make more sense to me if it were special-casing sending out change notifications based on whether the new string would actually be a change.
Incidentally, in an NSString setter, you almost always want copy rather than retain. I don't know the exact use case in this book, but if you just retain the string and it happens to be mutable, it can change behind your back and cause weird results. And if the string isn't mutable, copy is just an alias for retain.