Why isn't NSString answering to call by reference? - objective-c

I'm trying to change a variable of NSString type by calling a method. But it doesn't work. I tried this very same method with othe types and it worked perfectly fine. Can you help me out there?
Interface:
- (void) changeNSString: (NSString *) stringToChange;
Implementation:
- (void) changeNSString: (NSString *) stringToChange{
stringToChange = #"Test";
}
- (IBAction)actionBla:(id)sender {
NSString *myString = [NSString string];
[self changeNSString:myString];
NSLog(myString); //Returns nothing or empty string
}
======================
Edit: thanks to the help of CrimsonDiego, here is a working solution. Though I still have a little understanding issue: why is there no type-error when assigning a NSMutableString to a NSString var?
Interface:
- (void) changeNSMutableString: (NSMutableString *) stringToChange;
Implementation:
- (void) changeNSMutableString: (NSMutableString *) stringToChange{
[stringToChange setString:#"Test"]
}
- (IBAction)actionBla:(id)sender {
NSMutableString *myString = [NSMutableString string];
[self changeNSMutableString:myString];
self.myLabel.text = myString; //Why is there no type error???
}

You are assigning a new variable to a local variable within the function.
The local variable stringToChange references the same variable as myString at first, but when you assign #"Test" to stringToChange, it no longer references the original string.
The solution here would be to use NSMutableString, or have a class variable (or property) and use that instead of passing it to the function.

Your setting stringToChange as local in your function. Try the following:
- (NSString*) changeNSString: (NSString *) stringToChange{
stringToChange = #"Test";
return stringToChange;
}
- (IBAction)actionBla:(id)sender {
NSString *myString = [NSString string];
myString = [self changeNSString:myString];
NSLog(myString); //Returns nothing or empty string
}

I am no expert on Objective-C but it seems to me that stringToChange, as in "normal" C, is a parameter that can contain a pointer to a string. The parameter is pass-by-value and thus, when you assign #"Test" to stringToChange you are actually only changing the local parameter stringToChange and not myString. If you want to change myString you have to change the parameter type to NSString ** and pass &myString as a parameter instead.

Related

Get a Objective-C method to return several NSStrings

I need to use a method that returns several strings, different ones, according to a value.
My code looks like:
- (void) returnStringsAccordingToSet:(NSString *)string1: (NSString *)string2: (NSInteger)setNo {
switch (setNo){
case 1:
if (generalStringSettings){
string1 = #"The first string";
string2 = #"The second string";
} else {
string1 = #"The first other string";
string2 = #"The second other string";
}
break;
case 2:
...
break;
case 3:
...
break;
}
}
I call that method with:
NSString *firstString = [[NSString alloc]init];
NSString *secondString = [[NSString alloc]init];
NSUInteger set = 1;
[self getStringsAccordingToSet: firstString: secondString: set];
I can't get it to work! All I get is empty strings. I've got a feeling that the call for the strings is somewhat wrong. Please help.
You can't mae it work because when you do
string1 = #"The first string";
you just override the local parameter and update its reference but nothing outside the callee is modified. So the value is changed just inside the scope of the function.
You should change the signature of the method to
- (NSArray*) returnStringsAccordingToSet:(NSString *)string1: (NSString *)string2: (NSInteger)setNo {
so that it returns a NSArray instead that nothing and then inside the function
case1:
return [NSArray arrayWithObjects:#"The first string",#"The second string",nil];
so you return the array and store it from the caller, then you can access the returned values.
NSArray *v = [returnStringAccordingTo: ..];
[v objectAtIndex:0]
Technically, since ObjectiveC is a superset of C, I guess it is possible to pass a pointer to a pointer to NSString by reference through (but this is discouraged in ObjC):
NSString *string = nil;
[self callMethod:&string];
-(void)callMethod:(NSString**)refToString {
*refToString = #"foobar";
You could do this by filling a NSArray, but just to show how C works I'll show the error that you made.
string1 and string2 are just pointers, so you can pass either mutable or immutable strings as arguments.But the original pointer never gets modified, for example:
NSString* string1=#"Hello"; // Let's suppose string1 is on the 0x8000 address
// And points to the 0x9000 address
NSString* string2=#"How are you?"; // string2: 0x1000 , pointing to 0x2000
[self returnStringsAccordingToSet: string1: string1 string2: string2];
Then when you call a method a copy is made for every pointer that you pass to the method:
- (void) returnStringsAccordingToSet:(NSString *)string1: (NSString *)string2: (NSInteger)setNo {
// string1 and string2 both point on the same address, but they are other pointers
// for example string1 is on 0x7000 and string2 on 0x7400, pointing to 0x9000
// and 0x2000
switch (setNo){
case 1:
if (generalStringSettings){
string1 = #"The first string"; // You just create another instance
string2 = #"The second string"; // of the string, but the original
// pointers aren't affected
} else {
string1 = #"The first other string";
string2 = #"The second other string";
}
break;
case 2:
...
break;
case 3:
...
break;
}
}
The solutions:
Pass a NSMutableString as argument and instead of assigning them, just modify the contents;
Pass a pointer to pointer;
Return a NSArray containing the new strings.
Update note: I personally don't use the method as a first thing to do. I use arrays if that's possible. Not sure if it's OK to use this with ARC. Some Apple methods use this approach to return error value, so it's good to be aware of it.
When method is called, those arguments you pass to function are copied (of course they are not getting the copy message, pointer is copied as far as you pass pointer), and those copies are used inside the function (or more technically initiated with the argument value). So if you change the copy (i.e. try to replace object on pointer, mutable objects are OK to modify) this will not reflect on the value. You may solve this using pointers (i.e. pass pointer to pointer).
So:
NSString* string = #"one string";
NSLog(string); // one string
// [self method:string]
- (void) method: (NSString*) string {
// Local copy created:
// NSString* string = copy of string (second pointer created, object not copied in memory)
string = #"other string";
NSLog(string); // other string
}
NSLog(string); // one string
You may do this like that:
- (void) method: (NSString**)str {
*str = #"Some string";
}
and then:
// string = "old vlalue"
NSString* string = #"old value";
// string = "Some string"
[self method:&string];

Self assignment for NSString category

I want to self assign an adjusted nsstring via category.
The example is a trim function:
I do not want that way:
NSString *theTempString = [theExampleString xTrim];
// ... go on doing stuff with theTempString
I want it this way:
[theExampleString xTrim];
// ... go on doing stuff with theExmapleString
The category looks like this:
- (void)xTrim
{
self = [self stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
}
The error that an assignment outside init is not possible - I understand that.
But now I'm interested in it, of course I can write an custom init methode, but is there no way around it like the one above???
Greetings and thanks,
matthias
You don't need to create a new NSString, the method already does that for you:
- (NSString *)xTrim
{
return [self stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
}
NSString is immutable so you need to assign it:
yourString = [yourString xTrim];
You cannot do that in a category on NSString, because NSString manages immutable strings, which means that the string can not be changed after it has been created.
You could implement it as category on NSMutableString:
- (void)xTrim
{
NSString *trimmed = [self stringByTrimmingCharactersInSet: [NSCharacterSet whitespaceCharacterSet]];
[self setString:trimmed]; // replaces the characters of "self" with those of "trimmed".
}
And if your question is: Can I write a method xTrim such that
[theExampleString xTrim]
replaces the receiver theExampleString with a new instance: No, that is not possible.

Pointers in method params - objective-c

How to pass pointer as param in method?
for example:
-(void) dosomething:(NSString *) simpleString :(NSMutableArray *) pointerToArray;
where simpleString is simple param, and pointerToArray is pointer to an array;
In Objective-C, strings and arrays are both classes. As you can see, they are already accessed through pointers. So you simply use them as the declaration says:
-(void) dosomething:(NSString *) simpleString :(NSMutableArray *) pointerToArray;
And you invoke like:
NSString *s = #"Hello, world";
NSMutableArray *a = [NSMutableArray arrayWithObjects: #"Hello", #"silly", #"example", nil];
[yourClass dosomething:s :a];
FWIW, the name of your method is dosomething::. It is customary to denote each parameter, so I would call it:
-(void) doSomethingWithString:(NSString *)greeting array:(NSMutableArray *)strings;
then the name is doSomethingWithString:array: which is much more readable, IMO. You
invoke it with:
[yourClass doSomethingWithString:s array:a];
Like this:
-(void) dosomething:(NSString *) simpleString :(NSMutableArray **) pointerToArray;
(Add a second '*' to the parameter type
In your method, you then do something like:
*pointerToArray = [NSMutableArray array];
For example:
NSString *localSimpleString;
NSMutableArray *localArray;
[self dosomething:localSimpleString :pointerToArray];

Have trouble appending to an NSMutableString

#interface MainView : UIView {
NSMutableString *mutableString;
}
#property (nonatomic, retain) NSMutableString *mutableString;
#end
#implementation MainView
#synthesize mutableString;
-(void) InitFunc {
self.mutableString=[[NSMutableString alloc] init];
}
-(void) AppendFunc:(*NString) alpha {
[self.mutableString stringByAppendingString:#"hello"];
NSLog(#"the appended String is: %#",self.mutableString);
int len=[self.mutableString length];
}
Hey Everyone,
i just want to know where I am doing wrong ??? . I tried this code but "mutableString" does not append any value (as value of "len"comes '0' and NSLog doesnt print any value for "mutableString"), though i ve searched on net for the solution, people implemented in the same fashion yet i dont know why my code isnt working.
Thanks in Advance
MGD
stringByAppendingString: creates new string. Use appendString: instead:
[mutableString appendString:#"hello"]
1) Your method names violate the naming conventions: use lower case letters to start with.
2) stringByAppendingString returns a new string as a result and doesn't modify your original. You should use [self.mutableString appendString:#"hello"]; instead.
3) Your init method is leaking. You should use mutableString = [[NSMutableString alloc] init]; and not use the dot syntax, as it will be retained otherwise (and a release would be missing).
Oh God, what a mess. The stringByAppendingString does not change the string, it creates and returns a new one:
// Sets str2 to “hello, world”, does not change str1.
NSMutableString *str1 = [NSMutableString stringWithString:#"hello, "];
NSString *str2 = [str1 stringByAppendingString:#"world"];
If you want to change the mutable string itself, use the appendString method:
// Does not return anything, changes str1 in place.
[str1 appendString:#"world"];
Also, this is a leak:
self.mutableString = [[NSMutableString alloc] init];
This is best written as:
mutableString = [[NSMutableString alloc] init];
…because using accessors in init and dealloc is not the best idea.

Is it necessary to assign a string to a variable before comparing it to another?

I want to compare the value of an NSString to the string "Wrong". Here is my code:
NSString *wrongTxt = [[NSString alloc] initWithFormat:#"Wrong"];
if( [statusString isEqualToString:wrongTxt] ){
doSomething;
}
Do I really have to create an NSString for "Wrong"?
Also, can I compare the value of a UILabel's text to a string without assigning the label value to a string?
Do I really have to create an NSString for "Wrong"?
No, why not just do:
if([statusString isEqualToString:#"Wrong"]){
//doSomething;
}
Using #"" simply creates a string literal, which is a valid NSString.
Also, can I compare the value of a UILabel.text to a string without assigning the label value to a string?
Yes, you can do something like:
UILabel *label = ...;
if([someString isEqualToString:label.text]) {
// Do stuff here
}
if ([statusString isEqualToString:#"Wrong"]) {
// do something
}
Brian, also worth throwing in here - the others are of course correct that you don't need to declare a string variable. However, next time you want to declare a string you don't need to do the following:
NSString *myString = [[NSString alloc] initWithFormat:#"SomeText"];
Although the above does work, it provides a retained NSString variable which you will then need to explicitly release after you've finished using it.
Next time you want a string variable you can use the "#" symbol in a much more convenient way:
NSString *myString = #"SomeText";
This will be autoreleased when you've finished with it so you'll avoid memory leaks too...
Hope that helps!
You can also use the NSString class methods which will also create an autoreleased instance and have more options like string formatting:
NSString *myString = [NSString stringWithString:#"abc"];
NSString *myString = [NSString stringWithFormat:#"abc %d efg", 42];