Pass an NSString variable to a function and modify it - iphone

I'm new in objective-c, this is my first post. This is my problem:
In my main file I have a variable: NSString *var;
Then I have a function:
-(BOOL) myfunction:(NSString *)myvar {
myvar = #"OK!";
return YES;
}
Now in the main file I do:
NSString *var;
BOOL control = myfunction:var;
if(control)
NSLog(#"var is: %#",var);
but the output is "var is: ". If I built in debug mode, and put a breakpoint at the start of function myfunction the memory address of myvar is equal to var, but after the assignment the address of myvar is different from the address of var. How can I solve it? thanks in advance!

While the answers given so far are correct. I think a more pertinent question is why you would want to do this.
It looks you want to have a defensive programming style where the return code indicates success or failure.
A more Objective-C like way to do this would be to pass the string and an NSError ** as a parameter and return the string or a nil to indicate failure as described in the Error Handling Programming Guide
So the way to write this would be:
- (NSString *)aStringWithError:(NSError **)outError {
returnString = #"OK";
if (!returnString) {
if (outError != NULL) {
NSString *myErrorDomain = #"com.company.app.errors";
NSInteger errNo = 1;
*outError = [[[NSError alloc] initWithDomain:myErrorDomain code:errNo userInfo:nil] autorelease];
}
}
return returnString;
}
And to use it:
NSError *error;
NSString *stringVariable = [self aStringWithError:&error];
if (!stringVariable) {
// The function failed, so it returned nil and the error details are in the error variable
NSLog(#"Call failed with error: %ld", [error code]);
}
This is a trivial example, but you can see that you can construct and return far more meaningful information about the error rather than just whether it succeeded or not.

You can also use NSMutableString.
-(BOOL) myfunction:(NSMutableString *)myvar {
[myvar setString:#"OK!"];
return YES;
}
and change your call site as follows:
NSMutableString *var = [NSMutableString string];
BOOL control = [self myfunction:var];
if(control)
NSLog(#"var is: %#",var);

Syntactically you need to fix your myFunction invocation like so:
BOOL control = [self myfunction:var];
The real problem here is that inside of myFunction you are assigning a string value to a local string variable only, whereas you want to change the underlying string that it points to. This is usually the wrong approach in Obj-C, but you can do it like so:
-(BOOL)myfunction:(NSString **)myvar
{
*myvar = #"OK!";
return YES;
}
NSString *var;
BOOL control = [self myfunction:&var];
if(control)
NSLog(#"var is: %#",var);

Related

retain with objective-c (again but with more precision)

I post this question again but with more precision this time,
first, I have this function who return a string, there is some error about memory management or this fonction is ok?
-(NSString *) motAvecCle:(NSString *) cle
{
NSString *motRetour;
motRetour = #"";
cle = [FonctionUtile concatener:[[tableauConfig singletonTableauConfig] langueAffichage] chaine2:#"_" chaine3:[FonctionUtile trim:[cle uppercaseString]] chaine4:#""];
motRetour = [FonctionUtile trim:[dictionnaireLangue objectForKey:cle]];
if (motRetour == nil) {
motRetour = #"Erreur";
}
return motRetour;
}
and when I call this fonction,
NSString *myString = #"";
myString = [self motAvecCle:#"fr"]; // I must do this?
myString = [[self motAvecCle:#"en"]retain]; //or do this?
thx again...
The method motAvecCle: returns an object you do not own. Therefore, at some point it is going to disappear. Whether you care or not depends on where myString is defined. If it's in the same scope:
-(void) foo
{
NSString *myString = [self motAvecCle:#"fr"];
// do some stuff
}
you do not want to retain it (except in one circumstance) because the reference will disappear when foo exits which means if you had retained it you'd need to release it again first.
The one circumstance for retaining is if you modify the object you got the string from i.e. self in this case. That might cause the string to go away (although probably not in your specific example).
If myString is an instance variable of the object, you do want to retain it because otherwise it will disappear (possibly) the next time the auto release pool is drained. However, before assigning the instance variable, you must be sure to release the old value of the instance variable, unless it's actually the same string you are assigning i.e. you need to do something like this:
-(void) foo
{
NSString *tmp = [[self motAvecCle:#"fr"] retain]; // it's a string, technically you should copy, not retain
[myString release];
myString = tmp;
// do some stuff
}
Since you'd have to do that every time you want to assign the ivar, it's normal to create an accessor e.g.
-(void) setMyString: (NSString*) newValue
{
NSString* tmp = [newValue copy];
[myString release];
myString = tmp;
}
-(void) foo
{
[self setMyString: [self motAvecCle:#"fr"]];
// do some stuff
}
If you use properties, you can use #synthesize to create the accessors.

Can't get rid of this warning?

I'm getting this warning "Format not a string literal and no format arguments? Any ideas?
-(BOOL)isFirstPointReached{
NSString *firstPoint = [NSString stringWithFormat:[pointsToFillArray objectAtIndex:0]];
NSString *lastPoint = [NSString stringWithFormat:[pointsToFillArray lastObject]];
if([firstPoint isEqualToString:lastPoint]){
return YES;
}
else{
return NO;
}
}
A few points...
The pointsToFillArray is an array of objects and the compiler does not know if it contains NSStrings or any other type of object. To get rid of the error you would cast it to (NSString*)
Secondly, the stringWithFormat is normally used to create a string from a few different pieces of data and does not need to be used in this case
Thirdly, you could just create pointers to the objects within the array and then do your check
The following should work for you:
NSString *firstPoint = (NSString*)[pointsToFillArray objectAtIndex:0];
NSString *lastPoint = (NSString*)[pointsToFillArray lastObject];
if ([firstPoint isEqualToString:lastPoint]) {
return YES;
}

method with 2 return values

I want to call a method which returns two values
basically lets say my method is like the below (want to return 2 values)
NSString* myfunc
{
NSString *myString = #"MYDATA";
NSString *myString2 = #"MYDATA2";
return myString;
return myString2;
}
So when i call it, i would use??
NSString* Value1 = [self myfunc:mystring];
NSString* Value2 = [self myfunc:mystring2];
I guess im doing something wrong with it, can anyone help me out?
Thanks
You can only return 1 value. That value can be a struct or an object or a simple type. If you return a struct or object it can contain multiple values.
The other way to return multiple values is with out parameters. Pass by reference or pointer in C.
Here is a code snippet showing how you could return a struct containing two NSStrings:
typedef struct {
NSString* str1;
NSString* str2;
} TwoStrings;
TwoStrings myfunc(void) {
TwoStrings result;
result.str1 = #"data";
result.str2 = #"more";
return result;
}
And call it like this:
TwoStrings twoStrs = myfunc();
NSLog(#"str1 = %#, str2 = %#", twoStrs.str1, twoStrs.str2);
You need to be careful with memory management when returning pointers even if they are wrapped inside a struct. In Objective-C the convention is that functions return autoreleased objects (unless the method name starts with create/new/alloc/copy).
You have a few options:
NSArray: Just return an array. Pretty simple.
Pointers: Pass in two pointers, and write to them instead of returning anything. Make sure to check for NULL!
Structure: Create a struct that has two fields, one for each thing you want to return, and return one of that struct.
Object: Same a structure, but create a full NSObject subclass.
NSDictionary: Similar to NSArray, but removes the need to use magic ordering of the values.
As you can only return one value/object, maybe wrap them up in an array:
-(NSArray*) arrayFromMyFunc
{
NSString *myString = #"MYDATA";
NSString *myString2 = #"MYDATA2";
return [NSArray arrayWithObjects:myString,myString2,nil];
}
You can then use it like this:
NSArray *arr = [self arrayFromMyFunc];
NSString *value1 = [arr objectAtIndex:0];
NSString *value2 = [arr objectAtIndex:1];
You could pass results back by reference, but this is easy to get wrong (syntactically, semantically, and from memory management point of view).
Edit One more thing: Make sure that you really need two return values. If they are quite independent, two separate function are often the better choice - better reusabilty and mentainable. Just in case you are making this as a matter of premature optimization. :-)
You can only directly return one value from a function. But there is a way of doing it.
-(void) myfuncWithVal1:(NSString**)val1 andVal2:(NSString**)val2
{
*val1 = #"MYDATA";
*val2 = #"MYDATA2";
}
Then to call it outside the method you'd use:
NSString* a;
NSString* b;
[self myfuncWithVal1:&a andVal2:&b];
void myfunc(NSString **string1, NSString **string2)
{
*string1 = #"MYDATA";
*string2 = #"MYDATA2";
}
...
NSString *value1, *value2;
myfunc(&value1, &value2);
Remember that you need to pass a pointer to a pointer when working with strings and other objects.
Wrap the two strings in an NSArray:
- (NSArray*)myFunc
{
NSString *myString = #"MYDATA";
NSString *myString2 = #"MYDATA2";
return [NSArray arrayWithObjects:myString, myString2, nil];
}
NSArray *theArray = [self myFunc]
NSString *value1 = [theArray objectAtIndex:0];
NSString *value2 = [theArray] objectAtIndex:1];
I see everyone has mentioned an NSArray but I'd go with an NSDictionary so the values don't have to be added in order or even at all. This means it is able to handle a situation where you only want to return the second string.
- (NSDictionary*)myFunction {
NSString *myString1 = #"string1";
NSString *myString2 = #"string2";
return [NSDictionary dictionaryWithObjectsAndKeys: myString1, #"key1", myString2, #"key2", nil];
}
NSDictionary *myDictionary = [self myFunction]
NSString *string1 = [myDictionary objectForKey:#"key1"];
NSString *string2 = [myDictionary objectForKey:#"key2"];

variable parameter function - EXC_BAD_ACCESS when calling [obj release];

I have the following method:
(void)makeString:(NSString *)str1,... {
va_list strings;
NSString *innerText = [[NSString alloc] init];
NSString *tmpStr = [[NSString alloc] init];
if (str1) {
va_start(strings, str1);
while (tmpStr = va_arg(strings, id)) {
innerText = [innerText stringByAppendingString:tmpStr];
}
label.text = [str1 stringByAppendingString:innerText];
}
[tmpStr release];
}
I will eventually get to Objective C Memory Management reading, where I'm sure I will find the answer to this - probably related to pointers and copying - , but for now, can anyone explain why if I add [innerText release]; as the last line of this function, i get an EXC_BAD_ACCESS error at runtime?
First, your code is erroneous.
As far as I can see you are only concatenating the strings to assign the result to label.text.
I assume that label is an ivar, so label.text = … ist legal. Then the following should do it:
- (void)makeString: (NSString *)str1, ...
{
if (str1) {
NSString *tmpStr;
va_list strings;
va_start(strings, str1);
while (tmpStr = va_arg(strings, id)) {
str1 = [str1 stringByAppendingString: tmpStr];
}
label.text = str1;
}
}
Some notes:
You should not release any input parameter unless your method is about releasing something.
As the first answer stated, you should not release the result of stringByAppendingString: unless
you have retained it before.
[Update]
I changed the answer because it contained an error. label.text = str1 should retain str1 of course (if it wants to keep it). Especially the calling code should not retain str1 unless it wants to keep it for itself.
stringByAppendingString returns an autoreleased string, which is replacing your original assignment. So your release is not needed. But you are leaking memory with the two allocs above.
You should probably use [NSString initWithCString:va_arg(strings, id)] to assign the tmpStr too.

how to pass variable arguments to another method?

i have googled and came to know that how to use the variable arguments. but i want to pass my variable arguments to another method. i m getting errors. how to do that ?
-(void) aMethod:(NSString *) a, ... {
[self anotherMethod:a];
// i m doing this but getting error. how to pass complete vararg to anotherMethod
}
AFAIK ObjectiveC (just like C and C++) do not provide you with a syntax that allows what you directly have in mind.
The usual workaround is to create two versions of a function. One that may be called directly using ... and another one called by others functions passing the parameters in form of a va_list.
..
[obj aMethod:#"test this %d parameter", 1337);
[obj anotherMethod:#"test that %d parameter", 666);
..
-(void) aMethod:(NSString *)a, ...
{
va_list ap;
va_start(ap, a);
[self anotherMethod:a withParameters:ap];
va_end(ap);
}
-(void) anotherMethod:(NSString *)a, ...
{
va_list ap;
va_start(ap, a);
[self anotherMethod:a withParameters:ap];
va_end(ap);
}
-(void) anotherMethod:(NSString *)a withParameters:(va_list)valist
{
NSLog([[[NSString alloc] initWithFormat:a arguments:valist] autorelease]);
}
You cannot pass variadic arguments directly. But some of these methods provide an alternative that you can pass a va_list argument e.g.
#include <stdarg.h>
-(void)printFormat:(NSString*)format, ... {
// Won't work:
// NSString* str = [NSString stringWithFormat:format];
va_list vl;
va_start(vl, format);
NSString* str = [[[NSString alloc] initWithFormat:format arguments:vl] autorelease];
va_end(vl);
printf("%s", [str UTF8String]);
}
Have you considered setting up your arguments in either an array or dictionary, and coding conditionally?
-(void) aMethodWithArguments:(NSArray *)arguments {
for (id *object in arguments) {
if ([object isKindOfClass:fooClass]) {
//handler for objects that are foo
[self anotherMethod:object];
}
if ([object isKindOfClass:barClass]) {
//and so on...
[self yetAnotherMethod:object];
}
}
}
I think you could use macros to achieve same thing.
Let's say you wanna pass aMethod's variable arguments to another
-(void) aMethod:(NSString *) a, ... {
}
You could define your another 'method' using macro though it is not a real method:
#define anotherMethod(_a_,...) [self aMethod:_a_,##__VA_ARGS__]
This is my solution.