Использование 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;
}
}