objective c - sirve - Usando un NSString en una instrucción de cambio
serum hidratante facial (9)
Aquí es típicamente donde uso algo como una enumeración. Si tengo que administrar tantos valores, simplemente creo una enumeración con el mismo nombre que la cadena que habría pasado de otra manera y la paso allí, por ejemplo:
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;
}
¿Es posible utilizar una NSString
en una instrucción de switch
?
¿O es mejor usar solo if
/ else if
?
Como todos los demás han señalado, probablemente es más fácil de usar solo si / else, pero puede crear algo que se parezca mucho a una declaración de cambio. WSLObjectSwitch un proyecto en GitHub que hace exactamente eso: WSLObjectSwitch . Es una implementación bastante ingenua, no optimiza el uso de hashes, etc., pero funciona.
En respuesta y en apoyo de la respuesta de @ Cœur ... Aquí está lo mismo, pero escrito en Xcode 4.4+ / clang
/ Cualquier "sintaxis literal" que está aún más cerca de un simple urnario if, else
comparación (y ese es el punto, no es eso.....)
NSDictionary *actionD = @{ @"A" : ^{ NSLog(@"BlockA!"); },
@"B" : ^{ NSLog(@"BlockB!"); }};
((void(^)()) actionD[@"A"])();
BlockA!
o digamos, desea realizar un selector basado en el título de un botón ...
- (IBAction) multiButtonTarget:button {
((void (^)()) // cast
@{ @"Click?" : ^{ self.click; },
@"Quit!" : ^{ exit(-1); }} // define
[((NSButton*)button).title]) // select
(); // execute
}
¡Dejar! ⟹
exit -1
Breve, como w.string = kIvar == 0 ? @"StringA" : @"StringB";
w.string = kIvar == 0 ? @"StringA" : @"StringB";
¡Y mucho más útil, ya que puedes empujar bloques allí sin siquiera pensar en algún @selector
terrible (y limitado, y complicado)!
EDIT: Esto es más obviamente construido como tal:
[@[ @"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! ***
Tengo que admitir que estoy avergonzado EN este tipo de tonterías sintácticas. Otra opción es olvidarse de lo que es la cadena ... simplemente ejecútala, jajaja ...
[ @{ NSApplicationWillBecomeActiveNotification : @"slideIn",
NSApplicationDidResignActiveNotification : @"slideOut" } each:^( id key, id obj ) {
[w observeObject:NSApp forName:obj calling: NSSelectorFromString ( obj ) ];
}];
o tomando la palabra de la interfaz de usuario para ello, literalmente ..
- (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];
}
La instrucción Switch no funcionaría con NSString: solo funciona con int.
Si la declaración / Else es demasiado código y, a menudo, no es óptimo.
La solución óptima es utilizar un NSDictionary indexado por las posibilidades de NSString (u otros objetos). Entonces usted accede directamente al valor / función correcto.
Ejemplo 1, cuando desea probar para @ "A" o @ "B" y realizar el método A o el método B:
NSDictionary *action = @{@"A" : [NSValue valueWithPointer:@selector(methodA)],
@"B" : [NSValue valueWithPointer:@selector(methodB)],
};
[self performSelector:[action[stringToTest] pointerValue]];
Ejemplo 2, cuando desea probar para @ "A" o @ "B" y realizar el bloque A o el bloque B:
NSDictionary *action = @{@"A" : ^{ NSLog (@"Block A"); },
@"B" : ^{ NSLog (@"Block B"); },
};
((void (^)())action[stringToTest])();
La instrucción switch requiere constantes de enteros para sus casos, por lo que NSString no se puede usar aquí, por lo que parece que tiene que optar por la opción if / else.
Un punto más es que debe comparar los NSStrings usando el método isEqualToString: o compare:, por lo que incluso si los valores de puntero se han permitido para los casos de conmutación, no podría usarlos de todos modos
Sé que llego un poco tarde a la fiesta, pero aquí está mi presentación para una declaración de cambio de objetivo c. Es un poco complejo, así que ten cuidado con las macros feas.
caracteristicas:
- parece una declaración de cambio
- Tiene atomicidad de hilo incorporada
- Funciona con todos los objetos object-c, no solo
NSString
(usando el selector-isEqual:
- También podría extenderse para trabajar con tipos C, salvo las
struct
(ya que no tienen operador==
) - Solo se puede evaluar una etiqueta de caso por instrucción de cambio (no se requiere
break
) - No hay posibilidades de bucle infinito si no se selecciona ningún caso (no se requiere
break
) - Solo se reserva un nombre de variable,
____dontuse_switch_var
(todos los demás están en el ámbito estático y se pueden sobrescribir en el ámbito local) - No produce ningún problema de retención impar (utiliza
__weak
referencias__weak
)
Inconvenientes:
- Fuente muy difícil de entender.
- Todas las declaraciones de casos se evalúan antes de que se seleccione una (por lo tanto, coloque las más frecuentes en la parte superior)
- La atomicidad del hilo tiene un costo de rendimiento: no requiere ningún bloqueo, pero utiliza
NSThread
bastante. - Sin usar los corchetes
{
o}
, a Xcode no le gusta formatear las declaraciones correctamente (esto se debe a la etiqueta implícita degoto
a allí). - No solo encabezado (requiere un archivo
.m
para funcionar, paraNSValue
débiles deNSValue
)
Ejemplo:
#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.");
}
}
}
}
Sin más preámbulos, aquí está el código (feo):
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__)
No voy a proporcionar documentación para esto, ya que es bastante autoexplicativo. Si es realmente tan difícil de entender, deje un comentario y documentaré el código.
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
Yo uso estas macros en mi aplicación.
#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;
}
}
inspirado en alex grey, creé un método de categoría que aplica filtros encadenados a su objeto:
.h
#import <Foundation/Foundation.h>
typedef id(^FilterBlock)(id element,NSUInteger idx, BOOL *stop);
@interface NSObject (Functional)
-(id)processByPerformingFilterBlocks:(NSArray *)filterBlocks;
@end
.metro
#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
Puedes usarlo como
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];
}];
pero también puedes hacer cosas más complicadas, como cálculos aplicados:
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);
puede cambiar fácilmente entre los botones para diferentes acciones usando sus etiquetas.
Ejemplo:
- (IBAction)addPost:(id)sender {
switch ([sender tag]) {
case 1:
break;
case 2:
break;
case 3:
break;
case 4:
break;
case 5:
break;
default:
break;
}
}