Get a Objective-C method to return several NSStrings - objective-c

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];

Related

What is the best way to initialise a variable in objective c, before a conditional statement?

I used to initialise variables before a conditional statement in the following way:
NSString *string = [[NSString alloc] init];
if (conditional statement) {
string = #"foo";
}
else{
string = #"bar";
}
But the Xcode Analyser complains:
"Value stored to 'string' during its initialization is never read"
So, I then tried a couple of different options:
A:
NSString *string = nil;
if (conditional statement) {
string = #"foo";
}
else{
string = #"bar";
}
B:
NSString *string = #"bar";
if (conditional statement) {
string = #"foo";
}
So my question is, what is the best way to initialise a variable in objective c, before a conditional statement?
UPDATE:
The variable itself is not used [read] in the conditional. Here is an example below...
NSString *string = [[NSString alloc] init];
if (x == 0) {
string = #"foo";
}
else{
string = #"bar";
}
UPDATE:
Based on Sven's answer, it seems like a good compromise is:
NSString *string;
if (x == 0) {
string = #"foo";
}
else{
string = #"bar";
}
A and B are both valid options. In the end it won't really matter, if you just assign string literals. The compiler might even generate the same code for both cases.
Of course if you assign something other than compile-time constants you need to be more careful. Depending on the side effects that happen in your computation only one or the other version will be correct.
In your case A you won't even have to nil-initialise the variable at first, the compiler is smart enough to see that it is initialised in any case. For patterns like this where you want to initialise an variable depending on some conditions it's actually a good idea to skip the initialisation where the variable is defined. Then the compiler can produce a warning if there is a code path where you forgot to initialise the variable.
Another option for this is to use the ternary operator ?::
NSString *string = condition ? #"foo" : #"bar";
This is not just shorter to write, but also makes it immediately clear that the variable is initialised no matter what the condition is.

Sending 'void' to parameter of incompatible type 'id'

In Objective C, I have:
NSMutableArray *retVal = [[NSMutableArray alloc]initWithCapacity:1];
NSMutableString *justTest = [[NSMutableString alloc]initWithString:#"hello"];
unsigned char ch = //anything
[retVal insertObject:[justTest appendFormat:#"%hhu", ch] atIndex:0]; //error here
X Code 5.1.1 gives me an error in the 4th line(as mentioned as comment) as Sending 'void' to parameter of incompatible type 'id'
What am I doing wrong here? Any help is appreciated.
If you have a read of the Apple Documentation for NSMutableString you will find that the instance method appendString: doesn't actually return a value. It adds a structured string to the end of the receive and that's it.
So when you do [retVal insertObject:[justTest appendFormat:#"%hhu", ch] atIndex:0]; you are actually really doing [retVal insertObject:void atIndex:0]; and obviously you can't pass void in as a parameter which expects a valid object of id.
Here's the method declaration: - (void)appendFormat:(NSString *)format ... which you can see has a return type of void.
So what you need to be doing is you need to make the amendment to the string before you pass it into the insertObject:atIndex: method.
So change to
[justTest appendFormat:#"%hhu", ch]; // Append to existing string, DOESN'T return anything
[retVal insertObject:justTest atIndex:0]; // Pass string in as object at index
appendFormat doesn't return anything, it adjusts the mutable string. You need to do this:
NSMutableArray *retVal = [[NSMutableArray alloc]initWithCapacity:1];
NSMutableString *justTest = [[NSMutableString alloc]initWithString:#"hello"];
unsigned char ch = //anything
[justTest appendFormat:#"%hhu", ch]
[retVal insertObject:justTest atIndex:0]; //error here
[justTest appendFormat:#"%hhu", ch] return void.
You need:
[retVal insertObject:([justTest appendFormat:#"%hhu", ch],justTest) atIndex:0];

Why does a string pointer in Objective-C accept and return the value of the string and not a memory address?

For example in this code:
NSString *greeting = #"Hello";
NSLog(#"Greeting message: %#\n", greeting );
Greeting takes the value of a string, not an address. It also displays a string in NSLog and not an address. However, I thought pointers were supposed to be used like this:
int var = 20; /* actual variable declaration */
int *ip; /* pointer variable declaration */
ip = &var; /* store address of var in pointer variable*/
NSLog(#"Address of var variable: %x\n", &var );
/* address stored in pointer variable */
NSLog(#"Address stored in ip variable: %x\n", ip );
/* access the value using the pointer */
NSLog(#"Value of *ip variable: %d\n", *ip );
return 0;
I've always wondered why it's okay to do this with string pointers.
Well, that is something called Syntactic Sugar. What we are actually seeing; exactly doesn't happen like that under the hood.
For example, the code you have written:
NSString *greeting = #"Hello";
NSLog(#"Greeting message: %#\n", greeting );
When you pass greeting into NSLog, actually the following line of code gets executed.
NSLog(#"Greeting message: %#\n", [greeting description]); // description is a method defined in NSObject and NSString inherits it.
And even if you do:
NSString *greeting = #"Hello";
Now, greeting variable doesn't hold the contents of the string, neither it can because it is a pointer. It just holds the address of NSString #"Hello" where it is stored. And again, the assignment of pointer happens under the hood. The same is the case with the C language; we can write the following code in C, and it will compile without any errors:
char *string = "Hello, world!";
In C, the string "Hello, world!" is basically a character array, and string variable actually stores the pointer to this character array.
If you see the definition of NSLog method, it looks something like this:
FOUNDATION_EXPORT void NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
It clearly shows that NSLog message receives an NSString pointer. But what do we actually pass? We pass the NSString in it, but what is actually passed is a pointer to that NSString, again under the hood :)
I hope this helps you.
%# is the string formatter for NSObjects, calling the objects -description method. If you want the pointer address of the string object try %p.
NSString *string = #"A string";
NSLog(#"Object contents: %#", string);
NSLog(#"Object address: %p", string);

Why isn't NSString answering to call by reference?

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.

Objective-C switch defines contents of string

I've come from a java background, so i'm still getting to grips with some of the ways of doing things with Obj-C.
Depending on a number provided, I want an NSString variable to have different contents.
In java I would do something like this:
string foo;
switch (numberToSwtich){
case 1:
foo = "Something!";
break;
case 2:
foo = "Something Else!";
break;
}
Obviously there are two types of String in objective-c. NSString and NSSMutableString.
The difference being that you can change one at a later date. However, like java, can I initialize a NSString first then set its contents later or do I need to use an NSMutableString?
Something a bit like this...
NSString *aString = [[NSString alloc] init];
switch ([self getNumberOfSides]) {
case 1:
aString = #"A String";
break;
case 2:
aString = #"Another String";
break;
}
I'm aware there are other-ways of doing this, such as using an NSDictionary with numeric keys for example, but I would like to use a switch.
Thanks.
Your code is perfectly valid. There's no need to use NSMutableString.
Your code does leak memory, because you're not releasing the empty string you allocated using alloc. It's better to use the "string" class method for empty strings:
NSString *aString;
switch ([self getNumberOfSides]) {
case 1:
aString = #"A String";
break;
case 2:
aString = #"Another String";
break;
default:
aString = [NSString string];
break;
}