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.
Related
I have created a method that accept variadic arguments like
- (NSDictionary *) getImagePixelsAtLocation: (int) locations,...NS_REQUIRES_NIL_TERMINATION
but when I send message to this class method, the value of locations variable in called method is 0 (it does not matter how many arguments I pass).
The method receives scalar data types. My question is: Can we pass scalar variable to a method as variadic arguments? If yes, what am I doing wrong?
The method definition is:
- (NSDictionary *) getImagePixelsAtLocation: (int) pixel1,...
{
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
va_list args;
va_start(args, pixel1);
//processing logic
va_end(args);
}
This is how I am sending message:
[HSImageProcessing getImagePixelsAtLocation:1,2,nil];
Actually, there is one glaringly obvious flaw in your code there: when a variadic function is nil-terminated, the accepted type must be an object. You cannot compare an int to nil or NULL, or [NSNull null], which defeats the purpose of nil-termination, and effectively defeats all chances of iteration using the standard for and for-in loops. In addition, NSDictionary isn't too happy about storing non-object types, and will happily make the compiler complain. I've rewritten it to accept NSNumber*, and output a dictionary of numbers.
- (NSDictionary*) getImagePixelsAtLocation: (NSNumber*) pixel1,...
{
NSMutableDictionary *dict = [NSMutableDictionary dictionary];
va_list args;
va_start(args, pixel1);
//processing logic
for (NSNumber* arg = pixel1; arg != nil; arg = va_arg(args, NSNumber*)) {
[dict setObject:arg forKey:[NSString stringWithFormat:#"%i", [arg intValue]]];
}
va_end(args);
return dict;
}
All it takes is a little bit of extra code on your part to get that very same call working as well:
[self getImagePixelsAtLocation:[NSNumber numberWithInt:1],[NSNumber numberWithInt:2],nil];
I need to make init method with multiple NSStrings as argument.
Assume it looks like:
'-(id) initWithSomething: (NSString *) things, nil;'
How to recognize number of strings and write them into array ?
Regards
Use a variadic method:
//Interface
-(id) initWithSomething:(NSString*) arg1, ...;
//Implementation
-(id) initWithSomething:(NSString*) arg1, ... {
va_list args;
va_start(args, firstObject);
id obj;
for (obj = firstObject; obj != nil; obj = va_arg(args, id))
//Do stuff with each object.
va_end(args);
}
What you want is a Variadic function.
Basic objective-C syntax is as follows:
-(type)methodNameTakesInput:(type)param1 andMoreInput:(type)param2
So you can do
-(id)initWithString:(NSString *)str andOtherThing:(NSObject *)obj
Alternately, you could just pass the array you want:
-(id)initWithStuff:(NSArray *)arrayOfStuff
and fill the array as you normally would:
NSArray *arrayOfStuff = [NSArray arrayWithObjects:#"Strings!", #"More strings!", nil];
I'm looking at understanding objective-c and I came into a problem in tapping the screen and incrementing the count variable which I store in my appdelegate.
- (void)updateLabel:(NSInteger)num {
NSString *s = [[NSString alloc] initWithFormat:#"%#", num];
countLabel.text = s;
[s release];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
TestAppDelegate *aDel = (TestAppDelegate *)[UIApplication sharedApplication].delegate;
aDel.count++;
NSInteger num = aDel.count;
[self updateLabel:num];
}
I get the EXC_BAD_ACS which to me says I'm trying to access something I'm not. It looks like I cannot send updateLabel the num variable because the scope of the primitive type goes away at the end of the method and then when updateLabel tries to access it, I get the error? I wanted to know if I understood this concept correctly. Thanks.
// format specifier for integer is %d, not %#
NSString *s = [[NSString alloc] initWithFormat:#"%d", num];
num is not out of scope here. You are passing it by value to updateLabel. Please also check that countLabel is not already released when you are calling updateLabel.
And you can pass aDel.count directly to the updateLabel. There is no need of temporary num variable.
[self updateLabel:aDel.count];
The problem might be that NSInteger is not an object, see its definition by cmd-clicking the keyword:
#if __LP64__ || TARGET_OS_EMBEDDED || TARGET_OS_IPHONE …
typedef long NSInteger;
typedef unsigned long NSUInteger;
#else
typedef int NSInteger;
typedef unsigned int NSUInteger;
#endif
Which means that your method to update the label should look a bit like this:
- (void) updateLabel: (NSInteger) num {
countLabel.text = [NSString stringWithFormat:#"%i", num];
}
I wrote a singleton called SomeValues where I initialize a foo NSMutableArray. I then tried to write a function SetBFSV to set the values of this array from different control views.
#interface SomeValues : NSObject {
NSMutableArray *foo;}
+ (SomeValues *) sharedInstance;
#implementation
...
- (void) SetBFSV:(char)lbl ToVal:(long)BFSvl{
NSNumber *ValueBFSvl = [NSNumber numberWithLong:BFSvl];
NSString *Strlbl = [[NSString alloc] stringWithFormat:#"%s",lbl];
[foo setValue:ValueBFSvl forKey:Strlbl];
}
I know that setValue requires a NS object for both the value and the key, but I cannot declare my function as
(void) SetBFSV:(NSString)lbl ToVal:(NSNumber)BFSvl
because it doesn't compile with the error: "Can not use an object as parameter to a method".
In one ControlView I wrote then this piece of code:
SomeValues *myBFSV = [SomeValues sharedInstance];
const unsigned char *Bar = (unsigned char *)[#"Label1" UTF8String];
NSLog(#"The string %s", Bar);
[myBFSV SetBFSV:Bar ToVal:2.5];
When compiling I get a warning on the last line:
warning: passing argument 1 of 'SetBFSV:ToVal:' makes integer from pointer without a cast
Which integer? I'm getting stupid looking around for it. When running I get the print out from the NSLog, but right afterwards the program obviously crashes with this error:
'NSInvalidArgumentException', reason: '-[NSPlaceholderString stringWithFormat:]: unrecognized selector sent to instance 0x4e03280'
Clearly I'm passing something wrong to stringWithFormat but I cannot understand what.
Thanks for any help. Have a nice day!
/luca
Possible problems with your code (unless you have typos in it):
- (void) SetBFSV:(char)lbl ToVal:(long)BFSvl function expects char as its 1st parameter but you pass it a char* - your 1st warning probably comes from here
stringWithFormat is a class method so your code should look either:
NSString *Strlbl = [[NSString alloc] initWithFormat:#"%s",lbl];
[foo setValue:ValueBFSvl forKey:Strlbl]
or
NSString *Strlbl = [NSString stringWithFormat:#"%s",lbl];
[foo setValue:ValueBFSvl forKey:Strlbl]
since you try to use -stringWithFormat instead of +stringWithFormat you get a crash
If you pass char to a function then correct format specifier for it will be %c, not %s
You probably must release your Strlbl variable if you create it with alloc/init otherwise it will leak (but you must not release it if you use +stringWithFormat:)
For one, you can't use an object as a paramter:
(void) SetBFSV:(NSString)lbl ToVal:(NSNumber)BFSvl
You need to pass them as pointers, using the asterisk.
(void) SetBFSV:(NSString*)lbl ToVal:(NSNumber*)BFSvl
Furthermore, if you need to pass them as (char) and (long) your bit of code:
SomeValues *myBFSV = [SomeValues sharedInstance];
const unsigned char *Bar = (unsigned char *)[#"Label1" UTF8String];
NSLog(#"The string %s", Bar);
[myBFSV SetBFSV:Bar ToVal:2.5]; // in either case, this is a long. why are you passing a double here?
Passes *Bar as a pointer. You should really read up on the pointers and objects. IF you need to pass them in as (const char*) and (long) do it like this:
- (void) SetBFSV:(const char*)lbl ToVal:(long)BFSvl{
NSNumber *ValueBFSvl = [NSNumber numberWithLong:BFSvl];
NSString *Strlbl = [NSString stringWithUTF8String: lbl]; // your old code had a memory leak here. you need to either create an autorelease object, or release it after adding to `foo`
[foo setValue:ValueBFSvl forKey:Strlbl];
}
My Recommendation is to do the following:
- (void) SetBFSV:(NSString*)key ToVal:(long)val{
NSNumber *value = [NSNumber numberWithLong:val];
[foo setValue:value forKey:key];
}
And call it like the following (from your example):
SomeValues *myBFSV = [SomeValues sharedInstance];
NSString *Bar = [NSString stringWithString:#"Label1"];// this may not be necessary...
NSLog(#"The string %#", Bar);
[myBFSV SetBFSV:Bar ToVal:25]; // don't pass a decimal for a (long)
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);