代码之家  ›  专栏  ›  技术社区  ›  Thomas Clayson

在switch语句中使用NSString

  •  52
  • Thomas Clayson  · 技术社区  · 14 年前

    是否可以使用 NSString 在一个 switch 陈述?

    或者只是用 if / else if 是吗?

    9 回复  |  直到 12 年前
        1
  •  53
  •   Vladimir    14 年前

    switch语句的大小写需要整型常量,因此这里不能使用NSString,因此似乎必须使用if/else选项。

    还有一点是,必须使用isEqualToString:或compare:方法比较nsstring,因此即使允许开关情况下使用指针值,也不能使用它们

        2
  •  69
  •   TheTiger    11 年前

    我在应用程序中使用这些宏。

    #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;
        }
     }
    
        3
  •  12
  •   Alex Gray    9 年前

    回应并支持@Cur的回答。。这是相同的东西,但是用Xcode 4.4+编写/ clang / 无论什么 “字面语法”,即 更接近 到一个简单的骨灰盒 if, else 比较(这就是重点,不是吗…)

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

    BlockA!

    或者说,您希望根据按钮的标题执行选择器。。。

    - (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"; ,而且更有用,因为你可以在那里推块,甚至不考虑一些可怕的(有限的,复杂的) @selector !

    编辑:这更明显是这样构造的:

    [@[ @"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! ***

    我不得不承认,我 令人难堪 进入 这种句法上的愚蠢。 另一种选择是忘记字符串是什么。。执行它,哈哈。。。

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

    或者直接用用户界面的话来说。。

    - (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];
    }
    
        4
  •  10
  •   Cœur N0mi    10 年前

    Switch语句不能用于NSString:它只能用于int。

    If/Else语句是太多的代码,通常不是最佳的。

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

    示例1,当您要测试@“A”或@“B”并执行methodA或methodB时:

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

    示例2,当您要测试@“A”或@“B”并执行blockA或blockB时:

    NSDictionary *action = @{@"A" : ^{ NSLog (@"Block A"); },
                             @"B" : ^{ NSLog (@"Block B"); },
                             };
    ((void (^)())action[stringToTest])();
    
        5
  •  1
  •   vikingosegundo    12 年前

    受alex gray的启发,我创建了一个category方法,它将链式过滤器应用于其对象:

    .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);
    
        6
  •  1
  •   Richard J. Ross III    12 年前

    我知道我去派对有点晚了,但这是我提交的一份关于objective-c switch声明。这有点复杂,所以忍受难看的宏。

    特征:

    • 就像一个交换语句
    • 内置线程原子性
    • 适用于所有objective-c对象,而不仅仅是 NSString (使用 -isEqual: 选择器)
    • 也可以扩展到使用C-types,除非 struct s(因为他们没有 == 操作员)
    • 每个switch语句只能计算一个case标签( break 不需要)
    • 如果没有选择任何情况,就没有无限循环的机会( 打破 不需要)
    • 只保留一个变量名, ____dontuse_switch_var (所有其他的都在静态作用域中,可以在本地作用域中覆盖)
    • 不会导致任何奇怪的保留问题(使用 __weak 参考文献)

    缺点:

    • 非常 难以理解的来源
    • 所有case语句都是在选择一个之前评估的(因此将最频繁的语句放在顶部)
    • 线程原子性是以性能为代价的—它不需要任何锁,但它确实使用 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_开关.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__)
    

    我不打算为此提供文档,因为这是非常不言而喻的。如果真的很难理解,请留下注释,我将记录代码。

    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
    
        7
  •  1
  •   Ken    11 年前

    正如其他人所指出的,使用if/else可能是最简单的,但是 可以 创建类似switch语句的语句。我在GitHub上创建了一个项目,它正是这样做的: WSLObjectSwitch . 这是一个相当幼稚的实现,它没有优化使用散列等,但它确实有效。

        8
  •  0
  •   PopKernel    10 年前

    这通常是我使用枚举之类的东西的地方。如果必须管理这么多值,我只需创建一个枚举,该枚举的名称与否则传递给它的字符串的名称相同,然后在其中传递它,例如:

    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;
    }
    
        9
  •  0
  •   Gurjot Kalsi    8 年前

    您可以使用按钮的标记在不同操作的按钮之间轻松切换。

    例子:

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

    }