Utiliser un NSSsortingng dans une instruction de commutateur

Est-il possible d’utiliser un NSSsortingng dans une instruction switch ?

Ou est-il préférable de simplement utiliser if / else if ?

L’instruction switch nécessite des constantes de type entier pour les cas où NSSsortingng ne peut pas être utilisé ici, il semble donc que vous deviez choisir l’option if / else.

Un autre point est que vous devez comparer NSSsortingngs en utilisant la méthode isEqualToSsortingng: ou compare: ainsi, même si des valeurs de pointeur ont été autorisées pour les cas de commutation, vous ne pouvez pas les utiliser de toute façon.

J’utilise ces macros dans mon application.

 #define CASE(str) if ([__s__ isEqualToSsortingng:(str)]) #define SWITCH(s) for (NSSsortingng *__s__ = (s); ; ) #define DEFAULT SWITCH (ssortingng) { 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 écrit en Xcode 4.4+ / clang / Quelle que soit la “syntaxe littérale” qui est encore plus proche d’une simple urne if, else comparaison (et c’est le point, n’est pas il…..)

 NSDictionary *actionD = @{ @"A" : ^{ NSLog(@"BlockA!"); }, @"B" : ^{ NSLog(@"BlockB!"); }}; ((void(^)()) actionD[@"A"])(); 

BlockA!

ou disons que 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 } 

Quitter!exit -1

Bref, comme w.ssortingng = kIvar == 0 ? @"SsortingngA" : @"SsortingngB"; w.ssortingng = kIvar == 0 ? @"SsortingngA" : @"SsortingngB"; , et bien plus utile, car vous pouvez y insérer des blocs sans même penser à un @selector horrible (et limité, et alambiqué)!

EDIT: Ceci est plus clairement construit en tant que 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 got it! ****** You lose!!! *** *** You lose!!! *** *** Not sure! *** *** Not sure! ***

Je dois admettre que je suis embarrassant dans ce genre de bêtise syntaxique. Une autre option consiste à oublier 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: NSSelectorFromSsortingng ( obj ) ]; }]; 

ou en prenant le mot de l’interface pour cela, littéralement ..

 - (IBAction)setSomethingLiterallyWithSegmentedLabel:(id)sender { NSInteger selectedSegment = [sender selectedSegment]; BOOL isSelected = [sender isSelectedForSegment:selectedSegment]; BOOL *optionPtr = &isSelected; SEL fabricated = NSSelectorFromSsortingng ([NSSsortingng ssortingngWithFormat:@"set%@:",[sender labelForSegment:selectedSegment]]); [self performSelector:fabricated withValue:optionPtr]; } 

L’instruction switch ne fonctionnerait pas avec NSSsortingng: elle ne fonctionne qu’avec int.

L’instruction If / Else est trop de code et n’est souvent pas optimale.

La solution optimale consiste à utiliser un NSDictionary indexé par les possibilités NSSsortingng (ou d’autres objects). Ensuite, vous accédez directement à la bonne valeur / fonction.

Exemple 1, lorsque vous voulez tester @ “A” ou @ “B” et effectuez methodA ou methodB:

 NSDictionary *action = @{@"A" : [NSValue valueWithPointer:@selector(methodA)], @"B" : [NSValue valueWithPointer:@selector(methodB)], }; [self performSelector:[action[ssortingngToTest] pointerValue]]; 

Exemple 2, lorsque vous souhaitez tester @ “A” ou @ “B” et effectuer un blockA ou un blockB:

 NSDictionary *action = @{@"A" : ^{ NSLog (@"Block A"); }, @"B" : ^{ NSLog (@"Block B"); }, }; ((void (^)())action[ssortingngToTest])(); 

inspiré par alex grey, j’ai créé une méthode de catégorie, qui applique des filtres chaînés à son object:

.h

 #import  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 isEqualToSsortingng:@"YES"]) { NSLog(@"You did it"); *stop = YES;} return element;}; FilterBlock fb2 = ^id(id element, NSUInteger idx, BOOL *stop){ if ([element isEqualToSsortingng:@"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 les calculs chianed aplied:

 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 la fête, mais voici ma soumission pour une déclaration de changement d’objectives. C’est un peu complexe, alors supportez les macros laides.

Caractéristiques:

  • ressemble à une déclaration de changement
  • At-il une atomicité de thread intégrée
  • Fonctionne avec tous les objects -isEqual: -C, pas seulement NSSsortingng (en utilisant -isEqual: selector)
  • Peut être étendu pour fonctionner également avec les types C, en interdisant les struct (car elles n’ont pas d’opérateur == )
  • Une seule étiquette de cas peut être évaluée par déclaration de commutateur (la break n’est pas obligatoire)
  • Aucune chance de boucle infinie si aucune case n’est sélectionnée (la break n’est pas obligatoire)
  • Ne réserve qu’un nom de variable, ____dontuse_switch_var (tous les autres sont dans la scope statique et peuvent être remplacés dans la scope locale)
  • N’aboutit pas à des problèmes de conservation étrange (utilise des références __weak )

Désavantages:

  • Très difficile à comprendre la source
  • Toutes les instructions de cas sont évaluées avant que celle-ci soit sélectionnée (mettez donc les plus fréquentes en haut)
  • L’atsortingcité des threads a un coût de performance: elle ne nécessite aucun verrou, mais utilise beaucoup NSThread .
  • Sans les parenthèses { ou } , Xcode n’aime pas formater correctement les instructions (ceci est dû à l’étiquette goto implicite.
  • Pas d’en-tête uniquement (nécessite un fichier .m pour fonctionner, pour les NSValue faibles de NSValue )

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 (laid):

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 pour cela, car c'est assez explicite. Si c'est vraiment difficile à comprendre, laissez un commentaire et je documenterai le code.

NSValue + WeakRef.h:

 #import  @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 des hachages, etc., mais ça marche.

C’est généralement là que j’utilise quelque chose comme un enum. Si je dois gérer autant de valeurs, je crée juste une enum du même nom que la chaîne que j’aurais passée autrement et je la passerais 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 tags.

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

}