Использование NSString в операторе switch

Можно ли использовать NSString в инструкции switch?

Или лучше использовать if/else if?

Ответ 1

Оператор switch

требует для него целочисленных констант, поэтому NSString не может использоваться здесь, поэтому, похоже, вам нужно перейти на опцию if/else.

Еще один момент: вы должны сравнить NSStrings с помощью метода isEqualToString: или compare: так что даже если значения указателей были разрешены для случаев переключения, вы все равно не могли их использовать

Ответ 2

Я использую эти макросы в своем приложении.

#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

В ответ и в поддержку ответа @Cœur. Вот то же самое, но написано в 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
}

Quit!exit -1

Краткость, как w.string = kIvar == 0 ? @"StringA" : @"StringB";, и гораздо более полезная, так как вы можете заталкивать туда блоки, даже не думая о каком-то ужасном (и ограниченном и запутанном) @selector!

EDIT: это более очевидно построено как таковое:

[@[ @"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 этот синтаксический глупость. Другой вариант - забыть о том, что такое строка... просто выполните ее, lol...

[ @{ 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];
}

Ответ 4

Оператор switch не будет работать с NSString: он работает только с int.

Если/Else-оператор слишком много кода и часто не оптимален.

Оптимальное решение - использовать NSDictionary, индексированный возможностями NSString (или других объектов). Затем вы получаете прямой доступ к правильному значению/функции.

Пример 1, если вы хотите проверить для @ "A" или @ "B" и выполнить методA или 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

Как все остальные отметили, возможно, проще всего использовать if/else, но вы можете создать что-то похожее на оператор switch. Я создал проект на GitHub, который делает именно это: WSLObjectSwitch. Это довольно наивная реализация, она не оптимизирует использование хэшей и т.д., Но она работает.

Ответ 6

вдохновленный 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];
}];

но вы также можете делать более сложные вещи, например, при помощи простых вычислений:

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

Ответ 7

Я знаю, что я немного опаздываю на вечеринку, но вот мое выражение для оператора switch objective-c. Это немного сложнее, поэтому несите с уродливыми макросами.

Особенности:

  • выглядит как оператор switch
  • Встроенный поточный атомизм
  • Работает со всеми объектами objective-c, а не только NSString (с помощью селектора -isEqual:)
  • Может быть расширен и для работы с C-типами, запрещая struct (поскольку у них нет оператора ==)
  • Только один ярлык случая может быть оценен для оператора switch (break не требуется)
  • Нет шансов на бесконечный цикл, если ни один случай не выбран (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 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 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 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 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 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

Ответ 8

Обычно я использую нечто вроде перечисления. Если мне нужно управлять многими значениями, я просто создаю перечисление с тем же именем, что и строка, которую я бы передал в противном случае, и передал бы ее там, например:

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

вы можете легко переключаться между кнопками для разных действий с помощью своих тегов.

Пример:

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

}