Repointing a pointer inside an Objective-C method - objective-c

Firs of all, sorry if my english is not absolutely correct. It's not my native language but I'll try to explain myself the best I can.
I'm having a hard time trying to understand the following issue. Take in account the following code:
// On a class named SPOTest
- (void)referenceTest:(NSMutableString *)originalText
{
[originalText appendString:#" world!!!"]
}
// From another place
NSMutableString *myText = [NSMutableString stringWithString:#"Hello"];
NSLog(#"Contents of myText BEFORE: %#", myText);
SPOTest *myTest = [[SPOTest alloc] init];
[myTest referenceTest:myText];
NSLog(#"Contents of myText AFTER: %#", myText);
The output:
Contents of myText BEFORE: Hello
Contents of myText AFTER: Hello world!!!
I find it understandable. I'm working with pointers so if I change the thing and the end of a pointer, I'm changing that thing for all the pointers pointing to it. On the other hand, if I change the code and do this:
// On a class named SPOTest
- (void)referenceTest:(NSMutableString *)originalText
{
NSMutableString *newText = [NSMutableString stringWithString:#"Hello world!!!"];
originalText = newText;
}
// From another place
NSMutableString *myText = [NSMutableString stringWithString:#"Hello"];
NSLog(#"Contents of myText BEFORE: %#", myText);
SPOTest *myTest = [[SPOTest alloc] init];
[myTest referenceTest:myText];
NSLog(#"Contents of myText AFTER: %#", myText);
Then I get this:
Contents of myText BEFORE: Hello
Contents of myText AFTER: Hello
Why is that? I suppose the correct way to do this is to use a double indirection and an implementation similar to the one used with NSError mechanism but I want to understand why I'm obtaining this behavior. If I can change the contents and the end of myText pointer from the referenceTest: method in the first example, why can't I change the address of myText from the same method in the second example?
I know I'm missing something trivial but I can't find it and I'd like to understand this to better understand the reasoning behind NSError mechanism.
Thank you!

In the second case you're changing the local copy of that pointer. If you want to repoint it in the calling scope, you'd need to use a pointer to a pointer, i.e.:
- (void)referenceTest:(NSMutableString **)originalText
{
NSMutableString *newText = [NSMutableString stringWithString:#"Hello world!!!"];
*originalText = newText;
}
And call it thusly:
[myTest referenceTest:&myText];
And it is worth noting that stringWithString returns an autoreleased string, which means your function is too.

There's a difference between objects and pointers to objects.
Someone has created an NSMutableString object, which exists somewhere in memory. We don't really care where it is. That someone received an NSMutableString* which points to the NSMutableString object. A copy of that NSMutableString* was given to your method referenceTest. There can be any number of pointers to that NSMutableString object, but there is only one object.
The appendString method changes the NSMutableString object itself.
The stringWithString method creates a new NSMutableString object and returns a pointer to it. So now we have two objects, and newText is a pointer to the second one. When you assign newText to originalText, originalText becomes a pointer to the second NSMutableString object. However, originalText is just the parameter in your method. The pointer that the calling method holds isn't changed by this.

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. =)

Objective-C: How do you append a string to an NSMutableString?

URL Download
http://code.google.com/p/mwiphonesdk/source/browse/#svn/trunk/iMADE/PrepTasks/08
I have code at the location at the link above and I am using NSMutableString to append strings as data is downloaded. But I am finding that using appendString does not increase the length of the mutable string. I must be doing something wrong.
And when I am done I need to convert NSMutableString to NSString to return it. I have looked for examples to do this but I do not see any yet.
I am most familiar with Java and C# where there is a StringBuffer/StringBuilder which allows you to append pieces and them simply call toString at the end to get a String. It does not appear to be this easy in Objective-C.
NSString* str = [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding];
#pragma mark TODO Confirm this is appending a value to the mutable string
[self.mutableString appendString:str];
NSLog(#"str length: %d, %d", [str length], [self.mutableString length]);
Above is the section of code that calls appendString.
I see that str has a length > 0 but calling appendString does not increase the length of self.mutableString.
What am I doing wrong here?
As for having an NSMutableString* and needing to return an NSString*, the former is a subclass of the latter so anywhere you see an NSString* an NSMutableString* will suffice as-is.
Your code looks OK from what you've posted. The only thing I can think of is that perhaps there isn't any data to speak of when initializing the str variable. In such a case appending an empty string will do nothing to mutableString.
You'll also want to make sure self.mutableString has been properly allocated and initialized. You can send messages to NSObject*s that are nil which may be misleading when [self.mutableString length] returns 0.
I have fixed the problem. I simply was not initializing the NSMutableString value and it was transparently not doing anything.
Before appending the string I put the following code.
if (_mutableString == nil){
_mutableString = [[NSMutableString alloc] init];
}
Thanks everyone for answering. And it is good to know that I can use NSMutableString in place of NSString. (that is too easy) :)