是否可以在NSString
声明中使用switch
?
或者最好只使用if
/ else if
?
答案 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语句。它有点复杂,所以要忍受丑陋的宏。
功能强>
NSString
(使用-isEqual:
选择器)struct
s(因为他们没有==
运算符)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;
}
}