在switch语句中使用NSString

是否有可能在switch语句中使用NSString

或者, if只是使用if / else if ,会更好吗?

switch语句需要整数常量,所以NSString不能在这里使用,所以你似乎必须去if / else选项。

还有一点是您必须使用isEqualToString:或compare:方法比较NSString,所以即使指针值已被允许用于切换情况,您仍然无法使用它们

我在我的应用程序中使用这些macros。

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

作为回应,并支持@Cœur的答案..这是同样的事情,但写在Xcode 4.4 + / clang / 无论什么 “字面语法” 更接近于一个简单的榆树if, else比较(这就是重点,没有它…..)

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

BlockA!

或者说,你想要执行基于button标题的select器…

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

放弃!exit -1

简而言之,就像w.string = kIvar == 0 ? @"StringA" : @"StringB"; w.string = kIvar == 0 ? @"StringA" : @"StringB"; ,而且更有用,因为你可以在那里推块,甚至没有想到一些可怕的(和有限的,令人费解的)@select器!

编辑:这是更明显构造如此:

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

我不得不承认,对于这种语法上的愚蠢,我感到尴尬 。 另一个select是忘记什么是string..只是执行它,哈哈…

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

或者从UI的angular度来看,

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

Switch语句对于NSString不起作用:它只与int一起工作。

如果/其他语句是太多的代码,往往不是最佳的。

最佳的解决scheme是使用由NSString(或其他对象)可能性索引的NSDictionary。 然后你直接访问正确的值/function。

例1,当你想testing@“A”或@“B”并执行methodA或methodB时:

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

例2,当你想testing@“A”或@“B”并执行blockA或blockB时:

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

由alex灰色启发,我创build了一个类别方法,将链式filter应用于其对象:

。H

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

.M

 #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 

你可以使用它

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

但是你也可以做更复杂的事情,比如计算一下:

 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); 

我知道我对晚会有点迟,但是这里是我提交的一个Objective-C转换声明。 这有点复杂,所以忍受丑陋的macros。

特征:

  • 看起来像一个switch语句
  • 内置线程primefaces性
  • 适用于所有的Objective-C对象,而不仅仅是NSString (使用-isEqual:select器)
  • 可以扩展到使用Ctypes,禁止struct (因为他们没有==运算符)
  • 每个switch语句只能评估一个case标签(不需要break
  • 如果没有select任何情况,没有死循环的机会(不需要break
  • 只保留一个variables名, ____dontuse_switch_var (所有其他的都在静态范围内,可以在本地范围内覆盖)
  • 不会导致任何奇怪的保留问题(使用__weak引用)

缺点:

  • 很难理解源码
  • 所有的病例陈述都是在select病例之前进行评估的(所以最常见的病例在最上面)
  • 线程primefaces化的代价是性能 – 它不需要任何locking,但是它确实使用了NSThread
  • 不使用括号{} ,Xcode不喜欢格式化正确的语句(这是由于在那里的隐式的goto标签。
  • 不只是头文件(需要一个.m文件才能工作,对于NSValue弱引用)

例:

 #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."); } } } } 

不用再说了,这里是(丑陋的)代码:

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__) 

我不打算提供这方面的文件,因为这是不言而喻的。 如果真的很难理解,请留下评论,我将logging下这些代码。

NSValue + WeakRef.h:

 #import <Foundation/Foundation.h> @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 

正如其他人所指出的那样,使用if / else可能是最容易的,但是您可以创build一些看起来很像switch语句的东西。 我在GitHub上创build了一个完全相同的项目: WSLObjectSwitch 。 这是一个非常天真的实现,它不会优化使用哈希等,但它确实工作。

这通常是我使用类似枚举的地方。 如果我必须pipe理这么多的值,那么我只需要创build一个枚举,其名称与我将要传递的string相同,并传递给它,例如:

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

您可以使用标签轻松切换button之间的不同操作。

例如:

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

}