O Objective-C pode alternar no NSString?

166

Existe uma maneira mais inteligente de reescrever isso?

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
fonte
1
Não, a opção só funciona nos tipos int / bool / char / etc.
Chown
esta pergunta é um pouco semelhante a esta postada há apenas uma hora ( stackoverflow.com/questions/8161319/… )
Michael Dautermann
3
Existem várias maneiras alternativas de fazer isso. Por exemplo, carregue uma matriz com os valores e procure uma correspondência na matriz. Nenhuma é muito eficiente, mas reduz a duplicação de código.
Hot Licks
4
Como uma observação lateral, o novo idioma da Apple (Swift) agora permite comparar strings em uma instrução switch!
precisa saber é o seguinte
4
Mudar para Swift;)
tothemario 30/03

Respostas:

147

Infelizmente eles não podem. Esta é uma das melhores e mais procuradas utilizações de instruções de switch, por isso espero que elas entrem no (agora) Java (e outros) movimento!

Se você estiver fazendo nomes de cartões, talvez atribua a cada objeto de cartão um valor inteiro e ative-o. Ou talvez um enum, que é considerado um número e, portanto, pode ser ativado.

por exemplo

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

} CardType;

Feito dessa maneira, Ace seria igual ao caso 0, Dois como caso 1, etc.

Chris
fonte
4
@abbood Para obter mais informações sobre enum, consulte a publicação NS_ENUM & NS_OPTIONS de Mattt Thompson.
Basil Bourque
@abbood, o que seu comentário deveria significar? Parece que esta é uma resposta ruim, mas me parece ok. Você poderia explicar?
Alan Andrade
Pelo que entendi, CardTypenão pode ser igual a qualquer fechado @""por exemplo:[CardType isEqualToString:@"Three"]
Adromil Balais
120

Você pode configurar um dicionário de blocos, assim:

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 ter uma seção 'padrão', substitua a última linha por:

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

Espero que a Apple ensine 'trocar' alguns novos truques.

Graham Perks
fonte
35
Não sei dizer se isso é realmente desagradável ou muito legal. Nunca teria pensado em fazer isso, obrigado.
Endy
2
Enquanto estamos fazendo coisas estranhas como essa, por que não criar sua própria classe que agrupa um NSDictionary cheio de chaves NSString para objetos de bloco e depois fornece outro bloco para casos padrão? Você pode até dar suporte à notação subscrita.
ArtOfWarfare
1
Pontos extras se você criar uma subclasse NSDictionary apenas para isso: P
CommaToast
2
Sob o capô, é assim que o C # faz isso para declarações de comutadores grandes.
Hank Schultz
78

Para mim, uma maneira fácil e agradável:

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
fonte
1
Eu gosto disso. Ele atende às necessidades de quem procura uma resposta para esse problema, não é preciso digitar muito mais do que uma opção semelhante levaria em javascript e é legível por humanos.
ew Parris
4
Eu não compararia esse hack com o switch JS. O que acontece se o próximo programador adicionar um item entre o item1 e o item2? Muito potencial para a introdução de bugs
Aras
é um truque legal, então eu dou-lhe os polegares para o esforço :)
Aras
@Aras Se o próximo programador precisar adicionar uma nova entrada, eles a adicionarão ao final da matriz com uma nova declaração de caso no final para lidar com isso. Portanto, @ "item0" pode ser adicionado após @ "item3" na matriz e adicione um caso 3: para lidar com isso.
precisa saber é o seguinte
1
Eu gosto totalmente do seu jeito. É muito legal. Estou escrevendo a categoria e preciso retornar o UIColor enquanto tenho uma string comigo.
Alix
11

Infelizmente, as instruções switch podem ser usadas apenas em tipos primitivos. Você tem algumas opções usando coleções, no entanto.

Provavelmente, a melhor opção seria armazenar cada valor como uma entrada em um 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
fonte
8

Um pouco tarde, mas para qualquer pessoa no futuro eu consegui fazer isso funcionar para mim

#define CASE(str) if ([__s__ isEqualToString:(str)])
#define SWITCH(s) for (NSString *__s__ = (s); ; )
#define DEFAULT
Newyork167
fonte
Isto é interessante. Você pode elaborar mais?
Chen Li Yong
6

Aqui está a maneira mais inteligente de escrever isso. É para usar um NSNumberFormatterno "estilo explicativo" :

NSString *cardName = ...;

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

Observe que o formatador de números deseja que a sequência seja minúscula; portanto, precisamos fazer isso antes de passá-la ao formatador.

Dave DeLong
fonte
5

Existem outras maneiras de fazer isso, mas switchnão é uma delas.

Se você tiver apenas algumas strings, como no seu exemplo, o código que você possui está correto. Se você tiver muitos casos, poderá armazenar as strings como chaves em um dicionário e procurar o valor correspondente:

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

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

POR MUITO TEMPO .. meu "ObjC Add-On" favorito éObjectMatcher

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

E funciona com não-strings, DEMASIADO ... em loops, até!

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.

O melhor de tudo é que existem SO poucos {...}, :s e ()s

Alex Gray
fonte
3

O objetivo-c não é diferente de c nesse aspecto, ele só pode ativar o que c pode (e os defprocs são como NSInteger, NSUInteger, pois eles acabam sendo digitados em um tipo integral).

Wikipedia:

sintaxe c :

A instrução switch faz com que o controle seja transferido para uma das várias instruções, dependendo do valor de uma expressão, que deve ter um tipo integral .

Tipos integrais :

Na ciência da computação, um número inteiro é um dado de tipo de dados integral, um tipo de dado que representa algum subconjunto finito dos números inteiros matemáticos. Os tipos de dados integrais podem ter tamanhos diferentes e podem ou não ter permissão para conter valores negativos.

chown
fonte
2

Estou um pouco atrasado para a festa, mas para responder à pergunta como indicado , há uma maneira mais inteligente:

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

Observe que indexOfObjectprocurará a correspondência usando isEqual:, exatamente como na pergunta.

ilya n.
fonte
2

Com base na idéia do @Graham Perks postada anteriormente, projetamos uma classe simples para tornar a troca de strings bastante simples e limpa.

@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

Você usaria assim:

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

O bloco correto será executado de acordo com a string.

Gist para esta solução

Chuck Krutsinger
fonte
0

Não posso comentar a resposta de cris na resposta @Cris, mas gostaria de dizer que:

Existe uma LIMITAÇÃO para o método @ cris:

typedef enum não aceita valores alfanuméricos

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

} CardType;

Então aqui está outro:

Link Stack over flow Vá para esta resposta de usuário "user1717750"

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

Desfrute de codificação .....

Ek SAD.
fonte