60

In Objective-C 2.0, is it possible to make a method where the argument is optional? That is, you can have a method call like this:

[aFraction print];

as well as this:

[aFraction print: someParameter];

in the same program.

Apple's Objective-C 2.0 Programming Language guide contrasts Obj-C with Python and seems to say this is not allowed. I'm still learning and I want to be sure. If it is possible, then what is the syntax, because my second code example does not work.

Update: OK, I just made two methods, both named "print".

header

-(void) print;
-(void) print: (BOOL) someSetting; 

implementation

-(void) print {
    [self print:0];
}

-(void) print: (BOOL) someSetting {
    BOOL sv;
    sv = someSetting;

    if ( sv ) {
        NSLog(@"cool stuff turned on");
    }
    else {
        NSLog(@"cool stuff turned off");
    }
}

the relevant program lines

    ...
    printParamFlag = TRUE;

// no parameter
    [aCodeHolder print];

// single parameter
    [aCodeHolder print:printParamFlag];
    ...

I can't believe that worked. Is there any reason I shouldn't do this?

willc2
  • 38,991
  • 25
  • 88
  • 99

5 Answers5

93

You can declare multiple methods:

- (void)print;
- (void)printWithParameter:(id)parameter;
- (void)printWithParameter:(id)parameter andColor:(NSColor *)color;

In the implementation you can do this:

- (void)print {
    [self printWithParameter:nil];
}

- (void)printWithParameter:(id)parameter {
    [self printWithParameter:nil andColor:[NSColor blackColor]];
}

Edit:

Please do not use print and print: at the same time. First of all, it doesn't indicate what the parameter is and secondly no one (ab)uses Objective-C this way. Most frameworks have very clear method names, this is one thing that makes Objective-C so pain-free to program with. A normal developer doesn't expect this kind of methods.

There are better names, for example:

- (void)printUsingBold:(BOOL)bold;
- (void)printHavingAllThatCoolStuffTurnedOn:(BOOL)coolStuff;
Georg Schölly
  • 124,188
  • 49
  • 220
  • 267
  • when I pass nil as an argument, I get a warning: "passing argument 1 of 'print:' makes integer from pointer without a cast". Should I just ignore it? – willc2 Feb 18 '09 at 17:39
  • Could you post the line where this error occurs? I'm not really sure what you're doing. This error should not happen. – Georg Schölly Feb 18 '09 at 22:51
  • you can use NIL if the parameter type is (id). In my code the parameter type was (BOOL). The solution was to change NIL to 0 since that's the type expected. I get it now. The multiple methods thing is great. – willc2 Feb 19 '09 at 00:49
  • You should use NULL instead of 0. nil => no instance, Nil => no class, NULL => no C data – Georg Schölly Feb 19 '09 at 09:32
  • 3
    In your specific case use NO instead of 0. BOOLs shouldn't be 0 or 1, but NO and YES. – Georg Schölly Feb 19 '09 at 09:34
  • So Objective-C supports function overloads, but not default parameters. – bobobobo Nov 04 '13 at 20:56
  • @bobobobo: No. Every method name is unique. If you want to use different argument types, you need a different name. For example `printWithParameter:(id)parameter andColor:(NSColor *)color;` has the method name `printWithParameter:andColor:`. – Georg Schölly Nov 05 '13 at 11:18
  • 1
    Oh, I see. So the "method name" in Obj-C is just the concatenated list of argument-specifiers. – bobobobo Nov 05 '13 at 16:10
  • Second nil in middle code should be parameter. – sicko Dec 30 '20 at 12:50
16

The way you did it is the right way to do it in Objective-C. It's used extensively in Cocoa itself. For example, some of NSString's initializers:

– initWithFormat:  
– initWithFormat:arguments:  
– initWithFormat:locale:  
– initWithFormat:locale:arguments:

The reason it works is because the : is part of the method name, so as far as the compiler is concerned, print and print: are completely different messages that are no more closely connected than "print" and "sprint".

However, the particular names of the methods you gave aren't a very good case for this, because it's unclear from the name what the parameter is (or what "print" by itself means if the parameter is what the object prints). It would be better to have, say, printFalseMessage and printMessageWithFlag:.

Chuck
  • 234,037
  • 30
  • 302
  • 389
8

Another option for multiple optional arguments is to pass them all in an NSDictionary:

- (void)someMethodWithOptions:(NSDictionary *)options;

then declare that the options dictionary may contain:

- key1, value must be a BlahObject
- key2, value must be a NonBlahObject

you can then allow any of the keys to be absent and even the dictionary itself could be nil.

Matt Gallagher
  • 14,858
  • 2
  • 41
  • 43
  • 2
    This is very common in Ruby. Creating a dictionary is a bit verbose in ObjC-Cocoa, though, so it's generally better to save this for large sets of related options rather than use it as a "shortcut" for argument passing — it would be more like a scenic route. You also lose type-checking. – Chuck Feb 20 '09 at 00:58
  • I think Apple uses it too for some methods, like in strings. – Georg Schölly Feb 20 '09 at 11:07
6

Slightly related you can have optional arguments. Meaning you can call a method with any number of arguments if you like. But that is a feature from C (varargs).

An example, is the NSArray message:

+ (id)arrayWithObjects:(id)firstObj, ...

Example of usage:

NSArray *myArray;
NSDate *aDate = [NSDate distantFuture];
NSValue *aValue = [NSNumber numberWithInt:5];
NSString *aString = @"a string";

myArray = [NSArray arrayWithObjects:aDate, aValue, aString, nil];

This is straight from the Apple docs. You indicate the end of all your arguments with a nil.

Erik Engheim
  • 8,182
  • 4
  • 36
  • 51
  • 2
    Using that to represent optional arguments rather than a variable-length list for one argument would be quite perverse. – Chuck Feb 19 '09 at 09:04
  • How to declare type of method? – anhduongt Jan 30 '12 at 06:30
  • + (id)arrayWithObjects:(NSArray *)params; <= like this? – anhduongt Jan 30 '12 at 06:31
  • @youshunei Variable argument lists are specified via three dots ("...") in the method declaration, and accessed via the va_list, va_start(), and va_end() commands (and possibly others?). In Adam's example, firstObj is actually a separate parameter from the variable argument list specified by ..., and also ending the argument list with a nil is specific to the NSArray's implementation of the arrayWithObjects: method, not a general rule for variable argument lists. See [NSString stringWithFormat:] for an example that does not require a nil value. – jk7 Aug 27 '16 at 19:00
2

To avoid the Parse issue: 'aVariable' used as the name of the previous parameter rather than as part of the selector you get from the compiler as a warning you should do:

- (void)printWithParameter:(BOOL)sv color:(NSColor *)color{
   // your cool code goes here
   if ( sv ) {
      NSLog(@"cool stuff turned on");
   }
   else {
      NSLog(@"cool stuff turned off");
   }
}

and you could call the method, such:

[self printWithParameter:NO color:[UIColor someColor]] // NO instead of 0

or

[self printWithParameter:YES color:[UIColor someColor]] // YES instead of 1
John Wilund
  • 339
  • 4
  • 13