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