Using an NSString in a switch statement

Is it possible to use an NSString in a switch statement?

Or is it better to just use if / else if?


Solution 1:

I use these macros in my app.

#define CASE(str)                       if ([__s__ isEqualToString:(str)]) 
#define SWITCH(s)                       for (NSString *__s__ = (s); ; )
#define DEFAULT   

SWITCH (string) {
    CASE (@"AAA") {
        break;
    }
    CASE (@"BBB") {
        break;
    }
    CASE (@"CCC") {
        break;
    }
    DEFAULT {
        break;
    }
 }

Solution 2:

switch statement requires integer constants for it cases so NSString cannot be used here, so it seems you have to go for if/else option.

One more point is you must compare NSStrings using isEqualToString: or compare: method, so even if pointer values have been allowed for switch cases you could not use them anyway

Solution 3:

In response and in support of @Cœur's answer.. Here is the same thing, but written in Xcode 4.4+ / clang / Whatever "literal syntax" which is even closer to a simple urnary if, else comparison (and that's the point, isnt it.....)

NSDictionary *actionD = @{ @"A" : ^{ NSLog(@"BlockA!"); }, 
                           @"B" : ^{ NSLog(@"BlockB!"); }};

((void(^)()) actionD[@"A"])(); 

BlockA!

or say, you want to perform a selector based on the title of a button...

- (IBAction) multiButtonTarget:button { 

((void (^)())                           // cast
  @{ @"Click?" : ^{ self.click; }, 
     @"Quit!"  : ^{   exit(-1); }}      // define
        [((NSButton*)button).title])    // select
                                    (); // execute
}

Quit!exit -1

Brief, like w.string = kIvar == 0 ? @"StringA" : @"StringB";, and far more useful, as you can shove blocks in there without even a thought of some dreadful (and limited, and convoluted) @selector!

EDIT: This is more obviously constructed as such:

[@[ @"YES", @"NO", @"SIRPOOPSALOT"] do:^(id maybe) {
    [maybe isEqual:@"YES"] ? ^{ NSLog(@"You got it!"); }()
  : [maybe isEqual:@"NO" ] ? ^{ NSLog(@"You lose!!!"); }() 
  :                          ^{ NSLog(@"Not sure!"); [self tryAgain]; }();
}]; 

*** You got it! ****** You lose!!! ****** Not sure! ***

I have to admit, I'm embarrassingly INTO this kind of syntactic silliness. Another option is to forget about what the string is.. just execute it, lol...

[ @{ NSApplicationWillBecomeActiveNotification : @"slideIn",
     NSApplicationDidResignActiveNotification  : @"slideOut" } each:^( id key, id obj ) {
    [w observeObject:NSApp forName:obj calling: NSSelectorFromString ( obj ) ];       
}];

or taking the UI's word for it, literally..

- (IBAction)setSomethingLiterallyWithSegmentedLabel:(id)sender {
   NSInteger selectedSegment = [sender selectedSegment];
   BOOL isSelected = [sender isSelectedForSegment:selectedSegment];
   BOOL *optionPtr = &isSelected;
   SEL fabricated = NSSelectorFromString
       ([NSString stringWithFormat:@"set%@:",[sender labelForSegment:selectedSegment]]);
   [self performSelector:fabricated withValue:optionPtr];
}

Solution 4:

Switch statement wouldn't work with NSString: it works only with int.

If/Else statement is too much code and often not optimal.

Optimal solution is to use an NSDictionary indexed by the NSString (or other objects) possibilities. Then you directly access the right value/function.

Example 1, when you want to test for @"A" or @"B" and perform methodA or methodB:

NSDictionary *action = @{@"A" : [NSValue valueWithPointer:@selector(methodA)],
                         @"B" : [NSValue valueWithPointer:@selector(methodB)],
                         };
[self performSelector:[action[stringToTest] pointerValue]];

Example 2, when you want to test for @"A" or @"B" and perform blockA or blockB:

NSDictionary *action = @{@"A" : ^{ NSLog (@"Block A"); },
                         @"B" : ^{ NSLog (@"Block B"); },
                         };
((void (^)())action[stringToTest])();