在switch语句中使用NSString

时间:2010-11-19 11:26:09

标签: objective-c nsstring switch-statement

是否可以在NSString声明中使用switch

或者最好只使用if / else if

9 个答案:

答案 0 :(得分:68)

我在我的应用中使用这些宏。

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

答案 1 :(得分:51)

switch语句需要整数常量,因此NSString不能在这里使用,所以看来你必须选择if / else选项。

还有一点是你必须使用isEqualToString:或compare:方法来比较NSStrings,所以即使已经允许指针值用于切换案例,你也无法使用它们

答案 2 :(得分:12)

作为回应并支持@Cœur的回答..这是相同的事情,但用Xcode 4.4+ / clang / 编写,无论“文字语法”是更接近到一个简单的urnary 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! ***

我必须承认,我尴尬地 INTO 这种句法愚蠢。 另一种选择是忘记字符串是什么......只需执行它,哈哈......

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

或者用UI的话来说,字面上......

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

答案 3 :(得分:9)

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

如果/ 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])();

答案 4 :(得分:1)

正如其他人都注意到的那样,使用if / else可能最简单,但可以创建看起来很像switch语句的东西。我在GitHub上创建了一个完全相同的项目:WSLObjectSwitch。这是一个非常天真的实现,它没有使用哈希等优化,但它确实有效。

答案 5 :(得分:1)

受到alex Gray的启发,我创建了一个类别方法,将链式过滤器应用于其对象:

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

但你也可以做更复杂的事情,比如适当的chianed计算:

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)

我知道我参加聚会有点晚了,但这是我提交的一个Objective-c switch语句。它有点复杂,所以要忍受丑陋的宏。

功能

  • 看起来像一样
  • 内置线程原子性
  • 适用于所有objective-c对象,而不仅仅是NSString(使用-isEqual:选择器)
  • 也可以扩展为使用C类型,禁止struct s(因为他们没有==运算符)
  • 每个switch语句只能评估一个case标签(不需要break
  • 如果没有选择任何案例,则无法无限循环(break不需要)
  • 仅保留一个变量名称____dontuse_switch_var(所有其他变量名称都在静态范围内,并且可以在本地范围内覆盖)
  • 不会导致任何奇怪的保留问题(使用__weak引用)

<强>缺点:

  • 非常难以理解的来源
  • 在选择一个案例陈述之前评估所有案例陈述(因此将最常见的陈述置于最顶层)
  • 线程原子性是以性能为代价的 - 它不需要任何锁定,但它确实非常广泛地使用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__)

我不会为此提供文档,因为它非常不言自明。如果真的很难理解,请留下评论&amp;我将记录代码。

<强> 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 :(得分:0)

这通常是我使用类似枚举的地方。如果我必须管理那么多值,我只需要创建一个与我将传递的字符串同名的枚举,然后将其传递给那里,例如:

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

答案 8 :(得分:0)

您可以使用标签轻松切换按钮以执行不同操作。

示例:

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

}