String allocation and instance variables - objective-c

I am quite new to programming and objective C and I am having a hard time grasping a concept here about allocation, memory management and how they tie in with instance variables. I created a category for NSString called isUrl to test for a prefix of "http:// in a string.
From there, I wanted to test this on a string. However, given that situation I couldn't figure out if I should:
1) Declare a new string as an instance variable with #property to synthesize the accessors
2) Allocate a new string in my implementation
3) Set my variable and allocate it
What would be the difference between these methods and why would I perhaps want to use one over the other? -
As a side note, I am working with a book by O'Reilly and have tried to find answers around the web, but not a lot of luck thus far. I seem to find examples of everything, but I an explanation of how or why they are used is more difficult.
1)
NSString* string = [[[NSString alloc]init]autorelease];
string = #"http://www.google.com";
if ( [string isUrl]) {
NSLog(#"Caption is a URL");
NSLog(#"URL %#",string);
string = nil;
2)
NSString* string = #"http://www.googl.com";
[string retain];
if ( [string isUrl]) {
NSLog(#"Caption is a URL");
NSLog(#"URL %#",string);
string = nil;
3) Something like this....
#synthesize string;
string.name = "http://www.google.com";
if ( [string.name isUrl]) {
NSLog(#"Caption is a URL");
NSLog(#"URL %#",string);
string = nil;

String literals don't need to be released or retained.
Properties should be used whenever a value associated with an object needs to exist outside of a single function call and you don't need your program to compile under 10.4 or earlier. By your use of nil, it appears you don't need the string to persist, so you should only use a local variable. That's what local variables are for.
NSString* string = #"http://www.google.com";
if ( [string isUrl]) {
NSLog(#"Caption is a URL");
NSLog(#"URL %#",string);
}
Apple's Objective-C memory management rules are quite simple. There's really only one rule, with a couple of corollaries.
NSString* string = [[[NSString alloc]init]autorelease];
string = #"http://www.google.com";
You don't need to allocate an object before assigning a different object to the same variable. The first object will simply get deallocated and serves no purpose.
string.name = "http://www.google.com";
"" denotes a C string. For an NSString, you must use the at symbol. The above should be
string.name = #"http://www.google.com";

Related

Pass by value vs Pass by reference

I have been looking into some basics over the last couple days and I realized that i never truly understood why pass-by-reference for NSString/NSMutableString did not work.
- (void)testing{
NSMutableString *abc = [NSMutableString stringWithString:#"ABC"];
[self testing:abc];
NSLog(#"%#",abc); // STILL ABC
}
-(void)testing:(NSMutableString *)str {
str = [NSMutableString stringWithString:#"HELP"];
}
How do i go about this? I want my testing method to be able to manipulate the String from the main method. I have been using this with Mutable Arrays, dictionary etc and works fine. Feels strange that I never realized how this works with Strings.
But the value gets changed in something like this which is a reference to the first string
NSMutableString *string1;
NSMutableString *string2;
string1 = [NSMutableString stringWithString: #"ABC"];
string2 = string1;
[string2 appendString: #" HELP"];
NSLog (#"string1 = %#", string1); // ABC HELP
NSLog (#"string2 = %#", string2); // ABC HELP
Like Java, Objective-C has only passing and assigning by value. Also like Java, objects are always behind pointers (you never put the object itself into a variable).
When you assign or pass an object pointer, the pointer is copied and points to the same object as the original pointer. That means, if the object is mutable (i.e. it has some method that mutates its contents), then you can mutate it through one pointer and see the effects through the other one. Mutation is always achieved by calling a method, or assigning to a field directly.
-(void)testing:(NSMutableString *)str {
[str setString:#"HELP"];
}
Assigning to a pointer never mutates the object it points to; rather, it makes the pointer point to another object.
I cannot in good conscious let this wrong answer linger on the internet.
Pass by reference in objective c is POSSIBLE; which is why it is better than Java.
Here's how:
- (void)testing
{
NSMutableString *abc = [NSMutableString stringWithString:#"ABC"];
[self testingHelper:&abc];
NSLog(#"%#", abc); // NOW HELP
}
- (void)testingHelper:(NSMutableString **)str {
*str = [NSMutableString stringWithString:#"HELP"];
}

NSArray and NSString

The book I'm currently reading has me write the following code :
-(IBAction)displaySomeText:(id)sender {
NSString *cow = #"Milk";
NSString *chicken = #"Egg";
NSString *goat = #"Butter";
NSArray *food = [NSArray arrayWithObjects:cow, chicken, goat, nil];
NSString *string = #"The shopping list is: ";
string = [string stringByAppendingString:[food componentsJoinedByString:#", "]];
[textView insertText:string];
}
I understand somewhat how arrays work but I need help understanding the following code
string = [string stringByAppendingString:[food componentsJoinedByString:#", "]];
I have never ever seen an instance where this is possible.
He has me create a 'string' object, from the NSString class, and then I'm doing this
string = [string stringByAppendingString:];
I'm confused. I have never seen an example where I create an object and then perform a method on the same object and store it in that exact same object.
For example, I know I can do this
NSSound *chirp;
chirp = [NSSound soundNamed:#"birdChirp.mp3"];
the above makes sense because I used the created object and performed a class method on it..
but I always assumed that the equivalent of the following code was NOT possible
chirp = [chirp methodNameEtc..];
I hope I explained my question well. If not I could always elaborate further.
I think this is the heart of your question "I'm confused. I have never seen an example where I create an object and then perform a method on the same object and store it in that exact same object."
To answer that question, your not actually 'storing it in the exact same object'. What you are doing is confusing pointers and objects.
Let's just look at this line:
string = [string stringByAppendingString:#"Hello"];
In this line 'string' is a pointer, not the object it points to. What this line of code is saying is:
"Object currently referenced by the pointer 'string', return to me a new NSString object whose text is your text with this text added. And when I get that new NSString object I ordered make the pointer 'string' point to that object instead."
string = [string stringByAppendingString:[food componentsJoinedByString:#", "]];
is the same as
NSString *tmpString = [food componentsJoinedByString:#", "];
string = [string stringByAppendingString:tmpString];
So in the example, the innermost square brackets are evaluated first, and then the outermost. Does that clarify it a bit? Thing of it like parentheses in math: (1*2*(1+2))... the innermost () get evaluated before you can determine that the real problem is 1*2*3. That is what is happening with [food componentsJoinedByString:#", "].
stringByAppendingString is not storing the string in the exact same object. It's one NSString object creating a new (autoreleased) NSString object.
There is no equivalent example for chirp (which is a NSSound object), unless you create your own category which extends NSSound with a method to return a brand new NSSound object.
Makes sense?
Oh, I also appreciate your Takizawa icon.

When not to alloc and init an NSString

Whenever I need to create a new NSString variable I always alloc and init it. It seems that there are times when you don't want to do this. How do you know when to alloc and init an NSString and when not to?
Whenever I need to create a new NSString variable I always alloc and init it.
No, that doesn't make sense.
The variable exists from the moment the program encounters the point where you declare it:
NSString *myString;
This variable is not an NSString. It is storage for a pointer to an NSString. That's what the * indicates: That this variable holds a pointer.
The NSString object exists only from the moment you create one:
[[NSString alloc] init];
and the pointer to that object is only in the variable from the moment you assign it there:
myString = [[NSString alloc] init];
//Or, initializing the variable in its declaration:
NSString *myString = [[NSString alloc] init];
Thus, if you're going to get a string object from somewhere else (e.g., substringWithRange:), you can skip creating a new, empty one, because you're just going to replace the pointer to the empty string with the pointer to the other one.
Sometimes you do want to create an empty string; for example, if you're going to obtain a bunch of strings one at a time (e.g., from an NSScanner) and want to concatenate some or all of them into one big string, you can create an empty mutable string (using alloc and init) and send it appendString: messages to do the concatenations.
You also need to release any object you create by alloc. This is one of the rules in the Memory Management Programming Guide.
If you want to initialise it to a known value, there is little point in using alloc, you can just use a string literal:
NSString* myStr = #"Some value";
If you want to initialise it with a format or whatever, but don't need it to stick around beyond the current autorelease pool lifetime, it's a bit neater to use the class convenience methods:
NSString* myTempStr = [NSString stringWithFormat:#"%d", myIntVar];
If you need its lifetime to go beyond that, either alloc/init or else add a retain to the previous call. I tend to slightly prefer the latter, but the two are pretty much equivalent. Either way you will need a balancing release later.
Note that, since NSString is not mutable, this sort of thing is not only unnecessary but actively wrong:
// don't do this!
NSString* myStr = [[NSString alloc] initWithString:#""];
myStr = someOtherStr;
since it leaks the initial placeholder value.
It seems that there are times when you don't want to do this.
I can't think of any time when I would want to alloc/init a NSString. Since NSStringgs are immutable, you pretty much always create new strings by one of:
convenience class method e.g.
NSString* foo = [NSString stringWithFormat:...];
literal
NSString* foo = #"literal";
NSString instance method
NSString* foo = [bar uppercaseString];
copy from mutable string
NSString* foo = [mutableBar copy]; // foo needs to be released or autoreleased in this case
I'm guessing that you are referring to using StringWithString or similar instead of initWithString? StringWithString alloc and inits for you under the hood and then returns an autoreleased string.
If you don't need to do any string manipulation other than to have the string, you can use NSString *str = #"string";
In general with iOS, the tighter you manage your memory the better. This means that if you don't need to return a string from a method, you should alloc init and then release it.
If you need to return a string, of course you'll need to return an autoreleased string. I don't think its any more complicated than that.

If I want to make a new instance of an object in a function whose pointer is passed by reference in it

- (void)createAString:(NSString **)str
{
*str = [NSString stringWithString:#"Hi all!"];
[*str autorelease]; // ???? is this right ?
}
How should I use release or autorelease ? I don't want to release outside of the function of course :)
...
NSString *createStr;
[self createAString:&createStr];
NSLog(#"%#", createStr);
You're correct that you'd generally want to return autoreleased (or the like) objects from out params when you use this form. Your assignment statement in the function that sets *str to a string:
*str = [NSString stringWithString:#"foo"];
is already doing the right thing, because that method returns an instance of NSString that the caller doesn't own. Just like you could return this string object from your function without any further memory management, you can set it as the outparam as you've done. Your second snippet showing the call site is fine.
This said, I'm worried about a few things in your code that you should be sure you understand:
The value of str inside the method is still a **, and sending that a message (as you've done for the speculative autorelease) is nonsense. Be sure you fully understand doubly indirected pointers before using them too liberally. :) If you need to send str a message after creating it, send it to *str, which is what contains the NSString *.
Setting an outparam like this when the function returns void is not idiomatic Cocoa. You would normally just return the NSString * directly. Outparams are rare in Cocoa. (Usually just NSErrors get this treatment from framework calls. Otherwise they conventionally use name like getString to differentiate them from normal get accessors which don't use the word "get".)
I hope -stringWithString was just an example. That method is almost never used in practice, since it's equivalent (in this case) to just using a #"string literal" (although that would muddy your example).
Instead of using a double pointer, would it not be more elegant to use an NSMutableString instead?
- (void)createAString:(NSMutableString *)str
{
[str setString:#"Hi all!"];
}
....
NSMutableString *createStr = [[NSMutableString alloc] init];
[self createAString: createStr];
NSLog(#"%#", createStr);
[createStr release];
Or, even better, just have the createAString method return an NSString.
- (NSString *)createAString
{
return #"Hi all!"; // this is autoreleased automatically
}
I wouldn't want to presume that your needs are this simple, though. =)

In Obj-C How to update an existing NSString variable with a new formatted string?

How do i update an existing NSString variable with a new formatted string?
for example i have a variable like this:
String1 = [NSString new];
I want this string object to be updated from time to time with new formatted contents using the standard printf format.
I can initialise a new NSString using the initWithFormat: message but this is unavailable to already instantiated objects.
Any ideas? I'm thinking i can destroy the NSString each time and re-initialise a new one but is this the correct solution each time i need to update it?
Two options:
Create a new string every time with [[NSString alloc] initWithFormat:#"whatever"] and assign it to the variable. (Make sure you follow the memory management rules, which includes making sure the string's previous value is released. Of course, you'll need to follow those rules no matter how you tackle this problem.)
Create an NSMutableString and update the string with the mutating methods (appendFormat:, setString:, deleteCharactersInRange:, etc.). In this case, you're not just updating the variable, but the string itself.
Personally, I would use method 1, creating a new NSString every time. That way I don't have to fiddle with mutation and can just create a string with the precise value I want.
Strings in Cocoa are immutable objects.
This means that you won't change the same string but you will just free the old one and allocate a new NString with your updated content.
Of course this is not a problem since you will have a NSString* reference that will point to the last updated string.
NSString myString = [NSString stringWithFormat:...];
// do whatever
myString = [NSString stringWithFormat:..]
There is an NSMutableString which can be modified after the initialization. You can add a formatted string using appendStringWithFormat:. But if you want to replace the entire string, just create another. It doesn't cost much time and resources.
It depends on the taste, but I am against re-using a variable as Jack suggested. It often just confuses me, I prefer to create a new variable name whenever I create a new string, as in
NSString* myString = [NSString stringWithFormat:...];
// do whatever
NSString*myString2 = [NSString stringWithFormat:...];
Another thing is that new is not widely-used in Objective-C world. If you use that because it sounds familiar from your C++ background etc., consider using [[Class alloc] initWith...] instead.
The simplest thing to do is just always use "factory" methods that give you an autoreleased immutable object. In fact, you basically do that whenever you create an NSString that holds some static text.
For example:
NSString *myString = #"myString";
myString = [NSString stringWithFormat:#"myStringWithFormat"]; // Update it to something else
myString = [NSString stringWithFormat:#"The current date is: %#", [NSDate now]]; // Update it again
All three of the above examples are immutable strings that have a collective ref count of 0. You do not need to worry about releasing or allocating them.