¿Puede Objective-C cambiar a NSString?

166

¿Hay alguna forma más inteligente de reescribir esto?

if ([cardName isEqualToString:@"Six"]) {
    [self setValue:6];
} else if ([cardName isEqualToString:@"Seven"]) {
    [self setValue:7];
} else if ([cardName isEqualToString:@"Eight"]) {
    [self setValue:8];
} else if ([cardName isEqualToString:@"Nine"]) {
    [self setValue:9];
} 
James Raitsev
fuente
1
No, el interruptor solo funciona en los tipos int / bool / char / etc.
vestido el
esta pregunta es algo similar a esta publicada hace solo una hora ( stackoverflow.com/questions/8161319/… )
Michael Dautermann
3
Hay varias formas alternativas de hacerlo. Por ejemplo, cargue una matriz con los valores y busque una coincidencia en la matriz. Ninguno terriblemente eficiente, pero reducen la duplicación de código.
Hot Licks
44
Como nota al margen, el nuevo lenguaje de Apple (Swift) ahora permite comparar cadenas en una declaración de cambio.
jaredsmith
44
Cambiar a Swift;)
tothemario

Respuestas:

147

Lamentablemente no pueden. Esta es una de las mejores y más buscadas utilizaciones de declaraciones de cambio, ¡así que espero que se suban al (ahora) tren de Java (y otros)!

Si está haciendo nombres de tarjetas, quizás asigne a cada objeto de tarjeta un valor entero y actívelo. O quizás una enumeración, que se considera como un número y, por lo tanto, se puede activar.

p.ej

typedef enum{
  Ace, Two, Three, Four, Five ... Jack, Queen, King

} CardType;

Hecho de esta manera, Ace sería igual al caso 0, Dos como el caso 1, etc.

Chris
fuente
44
@abbood Para obtener más información sobre la enumeración, consulte la publicación NS_ENUM & NS_OPTIONS por Mattt Thompson.
Basil Bourque
@abbood, ¿qué se supone que significa tu comentario? Parece que esta es una mala respuesta, pero me parece bien. Podrías explicar ?
Alan Andrade
Según tengo entendido, CardTypeno puede ser igual a ninguno incluido, @""por ejemplo:[CardType isEqualToString:@"Three"]
Adromil Balais
120

Podrías configurar un diccionario de bloques, como este:

NSString *lookup = @"Hearts"; // The value you want to switch on

typedef void (^CaseBlock)();

// Squint and this looks like a proper switch!
NSDictionary *d = @{
    @"Diamonds": 
    ^{ 
        NSLog(@"Riches!"); 
    },
    @"Hearts":
    ^{ 
        self.hearts++;
        NSLog(@"Hearts!"); 
    },
    @"Clubs":
    ^{ 
        NSLog(@"Late night coding > late night dancing"); 
    },
    @"Spades":
    ^{ 
        NSLog(@"I'm digging it"); 
    }
};

((CaseBlock)d[lookup])(); // invoke the correct block of code

Para tener una sección 'predeterminada', reemplace la última línea con:

CaseBlock c = d[lookup];
if (c) c(); else { NSLog(@"Joker"); }

Esperemos que Apple le enseñe a 'cambiar' algunos trucos nuevos.

Graham Perks
fuente
35
No puedo decir si esto es realmente desagradable o realmente genial. Nunca hubiera pensado en hacer esto, gracias.
2013
2
Mientras hacemos cosas raras como esta, ¿por qué no hacer su propia clase que envuelva un NSDictionary lleno de claves NSString para objetos de bloque y luego proporcione otro bloque para casos predeterminados? Incluso puede hacer que admita la notación de subíndice.
ArtOfWarfare
1
Puntos adicionales si realiza una subclase NSDictionary solo por esto: P
CommaToast
2
Debajo del capó, esta es la forma en que C # lo hace para grandes declaraciones de cambio.
Hank Schultz
78

Para mí, una manera fácil y agradable:

NSString *theString = @"item3";   // The one we want to switch on
NSArray *items = @[@"item1", @"item2", @"item3"];
int item = [items indexOfObject:theString];
switch (item) {
    case 0:
       // Item 1
       break;
    case 1:
       // Item 2
       break;
    case 2:
       // Item 3
       break;
    default:
       break;
}
sbonkosky
fuente
1
Me gusta esto. Responde a las necesidades de la mayoría de las personas que buscan una respuesta a este problema, no requiere mucho más tipeo que un interruptor similar en javascript, y es legible para los humanos.
ew parris
44
No compararía este truco con el interruptor JS. ¿Qué sucede si el siguiente programador agrega un elemento entre item1 y item2? Demasiado potencial para introducir errores
Aras
Sin embargo, es un buen truco, así que les doy el visto bueno por el esfuerzo :)
Aras
@Aras Si el siguiente programador necesita agregar una nueva entrada, entonces la agregarán al final de la matriz con una nueva declaración de caso al final para manejarla. Entonces se puede agregar @ "item0" después de @ "item3" en la matriz, luego agregue un caso 3: para manejarlo.
sbonkosky
1
Me gusta mucho tu camino. Es muy aseado. Estoy escribiendo una categoría y necesito devolver UIColor mientras tengo una cadena conmigo.
Alix
11

Desafortunadamente, las declaraciones de cambio solo pueden usarse en tipos primitivos. Sin embargo, tiene algunas opciones para usar colecciones.

Probablemente la mejor opción sería almacenar cada valor como una entrada en un NSDictionary.

NSDictionary *stringToNumber = [NSDictionary dictionaryWithObjectsAndKeys:
                                              [NSNumber numberWithInt:6],@"Six",
                                              [NSNumber numberWithInt:7],@"Seven",
                                              [NSNumber numberWithInt:8],@"Eight",
                                              [NSNumber numberWithInt:9],@"Nine",
                                              nil];
NSNumber *number = [stringToNumber objectForKey:cardName];
if(number) [self setValue:[number intValue]];
ughoavgfhw
fuente
8

Un poco tarde, pero para cualquiera en el futuro pude hacer que esto funcione para mí

#define CASE(str) if ([__s__ isEqualToString:(str)])
#define SWITCH(s) for (NSString *__s__ = (s); ; )
#define DEFAULT
Nueva York167
fuente
Esto es interesante. ¿Puedes elaborar más?
Chen Li Yong
6

Aquí está la forma más inteligente de escribir eso. Es usar un NSNumberFormatteren el "estilo de deletreo" :

NSString *cardName = ...;

NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
[nf setNumberStyle:NSNumberFormatterSpellOutStyle];
NSNumber *n = [nf numberFromString:[cardName lowercaseString]];
[self setValue:[n intValue]];
[nf release];

Tenga en cuenta que el formateador de números quiere que la cadena esté en minúsculas, por lo que tenemos que hacerlo nosotros mismos antes de pasarla al formateador.

Dave DeLong
fuente
5

Hay otras formas de hacerlo, pero switchno es una de ellas.

Si solo tiene algunas cadenas, como en su ejemplo, el código que tiene está bien. Si tiene muchos casos, puede almacenar las cadenas como claves en un diccionario y buscar el valor correspondiente:

NSDictionary *cases = @{@"Six" : @6,
                        @"Seven" : @7,
                        //...
                       };

NSNumber *value = [cases objectForKey:cardName];
if (value != nil) {
    [self setValue:[value intValue]];
}
Caleb
fuente
4

POR MUCHO ... mi "Complemento ObjC" FAVORITO esObjectMatcher

objswitch(someObject)
    objcase(@"one") { // Nesting works.
        objswitch(@"b")
            objcase(@"a") printf("one/a");
            objcase(@"b") printf("one/b");
            endswitch // Any code can go here, including break/continue/return.
    }
    objcase(@"two") printf("It's TWO.");  // Can omit braces.
    objcase(@"three",     // Can have multiple values in one case.
        nil,              // nil can be a "case" value.
        [self self],      // "Case" values don't have to be constants.
        @"tres", @"trois") { printf("It's a THREE."); }
    defaultcase printf("None of the above."); // Optional default must be at end.
endswitch

Y funciona con sin cadenas, también ... en bucles, incluso!

for (id ifNumericWhatIsIt in @[@99, @0, @"shnitzel"])
    objswitch(ifNumericWhatIsIt)
        objkind(NSNumber)  printf("It's a NUMBER.... "); 
        objswitch([ifNumericWhatIsIt stringValue])
            objcase(@"3")   printf("It's THREE.\n"); 
            objcase(@"99")  printf("It's NINETY-NINE.\n"); 
            defaultcase     printf("some other Number.\n");
       endswitch
    defaultcase printf("It's something else entirely.\n");
endswitch

It's a NUMBER.... It's NINETY-NINE.
It's a NUMBER.... some other Number.
It's something else entirely.

Lo mejor de todo es que hay TAN pocos {...}'s, :' s y ()'s

Alex Gray
fuente
3

Objective-c no es diferente de c en este aspecto, solo puede activar lo que c puede (y los def de preproc como NSInteger, NSUInteger, ya que en última instancia están simplemente definidos como tipo integral).

Wikipedia:

c sintaxis :

La instrucción switch hace que el control se transfiera a una de varias instrucciones dependiendo del valor de una expresión, que debe tener un tipo integral .

Tipos integrales :

En informática, un número entero es un dato del tipo de datos integral, un tipo de datos que representa un subconjunto finito de los enteros matemáticos. Los tipos de datos integrales pueden ser de diferentes tamaños y se puede permitir o no que contengan valores negativos.

chown
fuente
2

Llego un poco tarde a la fiesta, pero para responder la pregunta como se dijo , hay una manera más inteligente:

NSInteger index = [@[@"Six", @"Seven", @"Eight", @"Nine"] indexOfObject:cardName];
if (index != NSNotFound) [self setValue: index + 6];

Tenga en cuenta que indexOfObjectbuscará la coincidencia utilizando isEqual:, exactamente como en la pregunta.

ilya n.
fuente
2

Basándose en la idea de @Graham Perks publicada anteriormente, diseñó una clase simple para hacer que el cambio de cadenas sea bastante simple y limpio.

@interface Switcher : NSObject

+ (void)switchOnString:(NSString *)tString
                 using:(NSDictionary<NSString *, CaseBlock> *)tCases
           withDefault:(CaseBlock)tDefaultBlock;

@end

@implementation Switcher

+ (void)switchOnString:(NSString *)tString
                 using:(NSDictionary<NSString *, CaseBlock> *)tCases
           withDefault:(CaseBlock)tDefaultBlock
{
    CaseBlock blockToExecute = tCases[tString];
    if (blockToExecute) {
        blockToExecute();
    } else {
        tDefaultBlock();
    }
}

@end

Lo usarías así:

[Switcher switchOnString:someString
                   using:@{
                               @"Spades":
                               ^{
                                   NSLog(@"Spades block");
                               },
                               @"Hearts":
                               ^{
                                   NSLog(@"Hearts block");
                               },
                               @"Clubs":
                               ^{
                                   NSLog(@"Clubs block");
                               },
                               @"Diamonds":
                               ^{
                                   NSLog(@"Diamonds block");
                               }
                           } withDefault:
                               ^{
                                   NSLog(@"Default block");
                               }
 ];

El bloque correcto se ejecutará de acuerdo con la cadena.

Gist para esta solución

Chuck Krutsinger
fuente
0

No puedo comentar sobre la respuesta de cris en @Cris answer pero me gustaría decir que:

Hay una LIMITACIÓN para el método de @ cris:

typedef enum no tomará valores alfanuméricos

typedef enum
{
  12Ace, 23Two, 23Three, 23Four, F22ive ... Jack, Queen, King

} CardType;

Así que aquí hay otro:

Enlace apilado sobre flujo Ir a esta respuesta de usuario "user1717750"

Puru
fuente
-1
typedef enum
{
    Six,
    Seven,
    Eight
} cardName;

- (void) switchcardName:(NSString *) param {
    switch([[cases objectForKey:param] intValue]) {
        case Six:
            NSLog(@"Six");
            break;
        case Seven:
            NSLog(@"Seven");
            break;
        case Eight:
            NSLog(@"Eight");
            break;
        default: 
            NSLog(@"Default");
            break;
    }
}

Disfruta de la codificación .....

Ek SAD.
fuente