Usar un NSString en una instrucción switch

¿Es posible usar un NSString en una instrucción switch ?

¿O es mejor simplemente usar if / else if ?

La instrucción switch requiere constantes enteras para los casos, por lo que NSString no se puede usar aquí, por lo que parece que tienes que ir a la opción if / else.

Un punto más es que debe comparar NSStrings usando isEqualToString: o comparar: método, por lo que incluso si se permitieron valores de puntero para los conmutadores, no podría usarlos de todos modos

Yo uso estas macros en mi aplicación.

 #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; } } 

En respuesta y en apoyo de la respuesta de @Cœur … Aquí está lo mismo, pero escrito en Xcode 4.4+ / clang / Lo que sea “syntax literal” que está incluso más cerca de una simple comparación de urnary if, else (y ese es el punto, no es eso…..)

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

BlockA!

o digamos, desea realizar un selector basado en el título de un botón …

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

¡Dejar!exit -1

Breve, como w.string = kIvar == 0 ? @"StringA" : @"StringB"; w.string = kIvar == 0 ? @"StringA" : @"StringB"; , y mucho más útil, ya que puedes meter bloques allí sin siquiera pensar en algún @selector terrible (y limitado y complicado).

EDITAR: Esto se construye más obviamente como tal:

 [@[ @"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 got it! ****** You got it! *** Pierdes *** You lose!!! *** *** You lose!!! ****** Not sure! *** *** Not sure! ***

Tengo que admitir que estoy avergonzante EN este tipo de tontería sintáctica. Otra opción es olvidarse de lo que es la cadena. Simplemente ejecútelo, lol …

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

o tomando la palabra de la interfaz de usuario para eso, literalmente …

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

La instrucción Switch no funcionaría con NSString: solo funciona con int.

Si / Else statement es demasiado código y, a menudo no es óptimo.

La solución óptima es usar un NSDictionary indexado por las posibilidades de NSString (u otros objetos). Luego accedes directamente al valor / función correcto.

Ejemplo 1, cuando desea probar @ “A” o @ “B” y realizar methodA o methodB:

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

Ejemplo 2, cuando desea probar @ “A” o @ “B” y realizar el bloqueA o el bloqueB:

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

inspirado en alex gray, creé un método de categoría, que aplica filtros encadenados a su objeto:

.h

 #import  typedef id(^FilterBlock)(id element,NSUInteger idx, BOOL *stop); @interface NSObject (Functional) -(id)processByPerformingFilterBlocks:(NSArray *)filterBlocks; @end 

.metro

 #import "NSObject+Functional.h" @implementation NSObject (Functional) -(id)processByPerformingFilterBlocks:(NSArray *)filterBlocks { __block id blockSelf = self; [filterBlocks enumerateObjectsUsingBlock:^( id (^block)(id,NSUInteger idx, BOOL*) , NSUInteger idx, BOOL *stop) { blockSelf = block(blockSelf, idx, stop); }]; return blockSelf; } @end 

Puedes usarlo como

 FilterBlock fb1 = ^id(id element, NSUInteger idx, BOOL *stop){ if ([element isEqualToString:@"YES"]) { NSLog(@"You did it"); *stop = YES;} return element;}; FilterBlock fb2 = ^id(id element, NSUInteger idx, BOOL *stop){ if ([element isEqualToString:@"NO"] ) { NSLog(@"Nope"); *stop = YES;} return element;}; NSArray *filter = @[ fb1, fb2 ]; NSArray *inputArray = @[@"NO",@"YES"]; [inputArray enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) { [obj processByPerformingFilterBlocks:filter]; }]; 

pero también puedes hacer cosas más complicadas, como cálculos de chianed aplicados:

 FilterBlock b1 = ^id(id element,NSUInteger idx, BOOL *stop) {return [NSNumber numberWithInteger:[(NSNumber *)element integerValue] *2 ];}; FilterBlock b2 = ^id(NSNumber* element,NSUInteger idx, BOOL *stop) { *stop = YES; return [NSNumber numberWithInteger:[element integerValue]*[element integerValue]]; }; FilterBlock b3 = ^id(NSNumber* element, NSUInteger idx,BOOL *stop) {return [NSNumber numberWithInteger:[element integerValue]*[element integerValue]];}; NSArray *filterBlocks = @[b1,b2, b3, b3, b3]; NSNumber *numberTwo = [NSNumber numberWithInteger:2]; NSNumber *numberTwoResult = [numberTwo processByPerformingFilterBlocks:filterBlocks]; NSLog(@"%@ %@", numberTwo, numberTwoResult); 

Sé que llego un poco tarde a la fiesta, pero aquí está mi presentación para una statement de cambio de objective-c. Es un poco complejo, así que ten cuidado con las feas macros.

caracteristicas:

  • parece una statement de cambio
  • Tiene una atomicidad de hilo incorporada
  • Funciona con todos los objetos Object-c, no solo NSString (usando el selector -isEqual:
  • Podría extenderse también para trabajar con tipos C, salvo struct s (ya que no tienen operador == )
  • Solo se puede evaluar una etiqueta de caso por statement de cambio (no se requiere break )
  • No hay oportunidades de ciclo infinito si no se selecciona ningún caso (no se requiere un corte)
  • Solo reserva un nombre de variable, ____dontuse_switch_var (todos los demás están en el ámbito estático y pueden sobrescribirse en el ámbito local)
  • No da como resultado problemas de retención extraños (utiliza referencias __weak )

Inconvenientes:

  • Fuente muy difícil de entender
  • Todas las declaraciones de casos se evalúan antes de que se seleccione (por lo tanto, coloque las más frecuentes en la parte superior)
  • La atomicidad del subproceso tiene un costo de rendimiento, no requiere ningún locking, pero sí utiliza NSThread bastante extensamente.
  • Sin usar corchetes { o } , a Xcode no le gusta formatear las declaraciones correctamente (esto se debe a la etiqueta de goto implícita).
  • No solo encabezado (requiere un archivo .m para trabajar, para NSValue débiles NSValue )

Ejemplo:

 #include "OBJC_SWITCH.h" int main() { NSArray *items = @[ @"A", @"B", @"C", @"D", @"E", @"F" ]; for (int i = 0; i < items.count; i++) { $switch(items[i]) { $case(@"A"): { NSLog(@"It was A!"); break; } $case(@"B"): // no brackets, no break, still works NSLog(@"It was B!"); $case(@"C"): // continue works as well, there's no difference { NSLog(@"It was C!"); continue; } $default: // brackets, but no break. { NSLog(@"Neither A, B, or C."); } } } } 

Sin más preámbulos, aquí está el código (feo):

OBJC_SWITCH.h:

 #import "NSValue+WeakRef.h" // mapping of threads to the values being switched on static NSMutableDictionary *____dontuse_switch_variable_dictionary; // boolean flag to indicate whether or not to stop switching static NSMutableDictionary *____dontuse_switch_bool_dictionary; // simple function to return the current thread's switch value static inline id current_thread_switch_value() { // simple initializer block static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ if (!____dontuse_switch_variable_dictionary) ____dontuse_switch_variable_dictionary = [NSMutableDictionary dictionary]; }); return [[____dontuse_switch_variable_dictionary objectForKey:[NSValue valueWithWeakObject:[NSThread currentThread]]] weakObjectValue]; } // simple function to set the current thread's switch value static inline void set_current_thread_switch_value(id val) { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ if (!____dontuse_switch_variable_dictionary) ____dontuse_switch_variable_dictionary = [NSMutableDictionary dictionary]; }); [____dontuse_switch_variable_dictionary setObject:[NSValue valueWithWeakObject:val] forKey:[NSValue valueWithWeakObject:[NSThread currentThread]]]; } // check if the current thread has switched yet static inline BOOL current_thread_has_switched() { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ if (!____dontuse_switch_bool_dictionary) ____dontuse_switch_bool_dictionary = [NSMutableDictionary dictionary]; }); return [[____dontuse_switch_bool_dictionary objectForKey:[NSValue valueWithWeakObject:[NSThread currentThread]]] boolValue]; } // set the current thread's switch state static inline void set_current_thread_has_switched(BOOL b) { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ if (!____dontuse_switch_bool_dictionary) ____dontuse_switch_bool_dictionary = [NSMutableDictionary dictionary]; }); [____dontuse_switch_bool_dictionary setObject:[NSNumber numberWithBool:b] forKey:[NSValue valueWithWeakObject:[NSThread currentThread]]]; } // concatenate two tokens #define $_concat(A, B) A ## B #define $concat(A, B) $_concat(A, B) /* start of switch statement */ #define $switch(value) { \ /* set this thread's switch value */ \ set_current_thread_switch_value(value); \ /* make sure we reset the switched value for the thread */ \ set_current_thread_has_switched(0); \ /* if statement to ensure that there is a scope after the `switch` */ \ } if (1) /* a case 'label' */ #define $case(value) \ /* make sure we haven't switched yet */ \ if(!current_thread_has_switched() && \ /* check to see if the values are equal */ \ [current_thread_switch_value() isEqual:value]) \ /* basically, we are creating a 'for' loop that only iterates once, so that we support the 'break' statement, without any harm */ \ /* this also sets the 'switched' value for this thread */ \ for (int ____dontuse_switch_var = 0; set_current_thread_has_switched(1), ____dontuse_switch_var != 1; ____dontuse_switch_var++) \ /* this creates the case label (which does nothing) so that it 'looks' like a switch statement. */ \ /* technically, you could to a goto with this, but I don't think anyone would purposely do that */ \ $concat(__objc_switch_label, __COUNTER__) /* the default 'label' */ #define $default \ /* this only evaluates if we haven't switched yet (obviously) */ \ if (!current_thread_has_switched()) \ /* exact same loop as for the 'case' statement, which sets that we have switched, and only executes once. */ \ for (int ____dontuse_switch_var = 0; set_current_thread_has_switched(1), ____dontuse_switch_var != 1; ____dontuse_switch_var++) \ /* once again, create a case label to make it look like a switch statement */ \ $concat(__objc_switch_label, __COUNTER__) 

No voy a proporcionar documentación para esto, ya que es bastante fácil de entender. Si es realmente tan difícil de entender, deje un comentario y documentaré el código.

NSValue + WeakRef.h:

 #import  @interface NSValue(WeakRef) +(id) valueWithWeakObject:(__weak id) val; -(id) initWithWeakObject:(__weak id) val; -(__weak id) weakObjectValue; @end 

NSValue + WeakRef.m:

 #import "NSValue+WeakRef.h" @interface ConcreteWeakValue : NSValue { __weak id _weakValue; } @end @implementation NSValue(WeakRef) +(id) valueWithWeakObject:(id) val { return [ConcreteWeakValue valueWithWeakObject:val]; } -(id) initWithWeakObject:(id)val { return [NSValue valueWithWeakObject:val]; } -(id) weakObjectValue { [self doesNotRecognizeSelector:_cmd]; return nil; } @end @implementation ConcreteWeakValue +(id) valueWithWeakObject:(__weak id)val { return [[self alloc] initWithWeakObject:val]; } -(id) initWithWeakObject:(__weak id)val { if ((self = [super init])) { _weakValue = val; } return self; } -(const char *) objCType { return @encode(__weak id); } -(__weak id) weakObjectValue { return _weakValue; } -(void) getValue:(void *)value { * ((__weak id *) value) = _weakValue; } -(BOOL) isEqual:(id)object { if (![object isKindOfClass:[self class]]) return NO; return [object weakObjectValue] == [self weakObjectValue]; } @end 

Como todos han señalado, probablemente sea más fácil usar if / else, pero puede crear algo que se parezca mucho a una statement de cambio. Creé un proyecto en GitHub que hace exactamente eso: WSLObjectSwitch . Es una implementación bastante ingenua, no optimiza el uso de hashes, etc., pero funciona.

Esto es típicamente donde uso algo así como una enumeración. Si tengo que administrar tantos valores, simplemente creo una enumeración con el mismo nombre que la cadena que hubiera pasado de otra manera y la paso allí, por ejemplo:

 enum { EGLFieldSelectionToolbarItem = 0, EGLTextSelectionToolbarItem, }; +(NSImage *)iconForIdentifier:(int)alias WithSize:(NSSize)size; //Alias should be an enum value with the same name NSImage *icon = [[NSImage alloc]initWithSize:size]; NSBezierPath *bezierPath = [NSBezierPath bezierPath]; [icon lockFocus]; switch (alias) { case EGLFieldSelectionToolbarItem: …//Drawing code break; case EGLTextSelectionToolbarItem: …//More drawing code default: break; } [bezierPath stroke]; [icon unlockFocus]; return icon; } 

puede cambiar fácilmente entre botones para diferentes acciones usando sus tags.

Ejemplo:

 - (IBAction)addPost:(id)sender { switch ([sender tag]) { case 1: break; case 2: break; case 3: break; case 4: break; case 5: break; default: break; } 

}