Est-il possible d'utiliser une variable NSString
dans une instruction switch
?
Ou vaut-il mieux utiliser if
/else if
?
l'instruction switch nécessite des constantes de nombre entier pour les cas où NSString ne peut pas être utilisé ici. Il semble donc que vous devez choisir l'option if/else.
Un autre point est que vous devez comparer NSStrings avec la méthode isEqualToString: ou compare: ainsi, même si les valeurs de pointeur ont été autorisées pour les cas de commutation, vous ne pouvez pas les utiliser.
J'utilise ces macros dans mon application.
#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;
}
}
En réponse et à l'appui de la réponse de @ Cœur .. Voici la même chose, mais écrite en Xcode 4.4+/clang
/ N'importe quelle "syntaxe littérale" qui est encore plus proche d'une simple comparaison de if, else
dans les urinaires (et c'est bien le but, n'est-ce pas .....)
NSDictionary *actionD = @{ @"A" : ^{ NSLog(@"BlockA!"); },
@"B" : ^{ NSLog(@"BlockB!"); }};
((void(^)()) actionD[@"A"])();
BlockA!
ou dire, vous voulez effectuer un sélecteur basé sur le titre d'un bouton ...
- (IBAction) multiButtonTarget:button {
((void (^)()) // cast
@{ @"Click?" : ^{ self.click; },
@"Quit!" : ^{ exit(-1); }} // define
[((NSButton*)button).title]) // select
(); // execute
}
Quit! ⟹
exit -1
Bref, comme w.string = kIvar == 0 ? @"StringA" : @"StringB";
, et bien plus utile, car vous pouvez y glisser des blocs sans même penser à quelque chose de terrible (et limité, et compliqué) @selector
!
EDIT: Ceci est plus évidemment construit comme tel:
[@[ @"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! ***
Je dois admettre que je suis embarrassant EN ce genre de bêtise syntaxique. Une autre option est d'oublier ce qu'est la chaîne. Il suffit de l'exécuter, lol ...
[ @{ NSApplicationWillBecomeActiveNotification : @"slideIn",
NSApplicationDidResignActiveNotification : @"slideOut" } each:^( id key, id obj ) {
[w observeObject:NSApp forName:obj calling: NSSelectorFromString ( obj ) ];
}];
ou en prenant la Parole de l'interface utilisateur pour cela, littéralement ..
- (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];
}
L'instruction Switch ne fonctionnerait pas avec NSString: elle ne fonctionnerait qu'avec int.
Si/Else est trop de code et n'est souvent pas optimal.
La solution optimale consiste à utiliser un NSDictionary indexé par les possibilités de NSString (ou d’autres objets). Ensuite, vous accédez directement à la bonne valeur/fonction.
Exemple 1, lorsque vous souhaitez tester @ "A" ou @ "B" et exécuter methodA ou methodB:
NSDictionary *action = @{@"A" : [NSValue valueWithPointer:@selector(methodA)],
@"B" : [NSValue valueWithPointer:@selector(methodB)],
};
[self performSelector:[action[stringToTest] pointerValue]];
Exemple 2, lorsque vous souhaitez tester @ "A" ou @ "B" et exécuter blockA ou blockB:
NSDictionary *action = @{@"A" : ^{ NSLog (@"Block A"); },
@"B" : ^{ NSLog (@"Block B"); },
};
((void (^)())action[stringToTest])();
inspiré par Alex Gray, j'ai créé une méthode de catégorie qui applique des filtres chaînés à son objet:
.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
Vous pouvez l'utiliser comme
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];
}];
mais vous pouvez aussi faire des choses plus compliquées, comme des calculs chianés appliqués:
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);
Je sais que je suis un peu en retard pour le parti, mais voici mon argument en faveur d’une déclaration d’object-c switch. C'est un peu complexe, alors supportez les macros laides.
Caractéristiques:
NSString
(en utilisant le sélecteur -isEqual:
)struct
s (car ils n'ont pas d'opérateur ==
)break
n'est pas obligatoire)break
n'est pas obligatoire)____dontuse_switch_var
(tous les autres sont dans la portée statique et peuvent être remplacés dans la portée locale)__weak
)Désavantages:
NSThread
assez souvent.{
ou }
, Xcode n'aime pas formater correctement les instructions (cela est dû au libellé implicite goto
qu'il contient..m
pour fonctionner, pour NSValue
références faibles)Exemple:
#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.");
}
}
}
}
Sans plus tarder, voici le code (moche):
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__)
Je ne vais pas fournir de documentation à ce sujet, car cela se passe d'explication. Si c'est vraiment difficile à comprendre, laissez un commentaire et je documenterai le code.
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
Comme tout le monde l’a noté, il est probablement plus simple d’utiliser if/else, mais vous pouvez créer quelque chose qui ressemble beaucoup à une instruction switch. J'ai créé un projet sur GitHub qui fait exactement cela: WSLObjectSwitch . C'est une implémentation assez naïve, elle n'optimise pas l'utilisation de hachages, etc., mais ça fonctionne.
Ceci est généralement où j'utilise quelque chose comme une enum. Si je dois gérer autant de valeurs, je crée simplement une énumération portant le même nom que la chaîne que j'aurais transmise autrement et la transmettrai ici, par exemple:
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;
}
vous pouvez facilement basculer entre les boutons pour différentes actions en utilisant leurs balises.
Exemple :
- (IBAction)addPost:(id)sender {
switch ([sender tag]) {
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
default:
break;
}
}