Kann Objective-C NSString einschalten?


166

Gibt es eine intelligentere Möglichkeit, dies umzuschreiben?

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

1
Nein, der Schalter funktioniert nur bei den Typen int / bool / char / etc.
Chown

Diese Frage ähnelt der Frage, die erst vor einer Stunde gestellt wurde ( stackoverflow.com/questions/8161319/… )
Michael Dautermann

3
Es gibt verschiedene Möglichkeiten, dies zu tun. Laden Sie beispielsweise ein Array mit den Werten und suchen Sie nach einer Übereinstimmung im Array. Keine schrecklich effizient, aber sie reduzieren die Codeduplizierung.
Hot Licks

4
Nebenbei bemerkt, Apples neue Sprache (Swift) ermöglicht jetzt den Vergleich von Zeichenfolgen in einer switch-Anweisung!
Jarmsmith

Antworten:


147

Leider können sie nicht. Dies ist eine der besten und gefragtesten Anwendungen von switch-Anweisungen. Hoffentlich hüpfen sie auf den (jetzt) ​​Java (und andere) Zug!

Wenn Sie Kartennamen verwenden, weisen Sie möglicherweise jedem Kartenobjekt einen ganzzahligen Wert zu und schalten Sie diesen ein. Oder vielleicht eine Aufzählung, die als Zahl betrachtet wird und daher eingeschaltet werden kann.

z.B

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

} CardType;

Auf diese Weise wäre Ace gleich Fall 0, Zwei als Fall 1 usw.


4
@abbood Weitere Informationen zu enum finden Sie in der Veröffentlichung NS_ENUM & NS_OPTIONS von Mattt Thompson.
Basil Bourque

@abbood was soll dein Kommentar bedeuten? Klingt so, als wäre dies eine schlechte Antwort, scheint mir aber in Ordnung zu sein. Könntest du erklären ?
Alan Andrade

Wie ich verstehe, CardTypekann nicht gleich einem beiliegenden @""zB sein:[CardType isEqualToString:@"Three"]
Adromil Balais

119

Sie können ein Wörterbuch mit Blöcken wie folgt einrichten:

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

Ersetzen Sie die letzte Zeile durch:

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

Hoffentlich bringt Apple ein paar neue Tricks bei.


34
Ich kann nicht sagen, ob das wirklich böse oder wirklich cool ist. Hätte nie daran gedacht, danke.
Endy

2
Während wir solche seltsamen Dinge tun, können Sie eine eigene Klasse erstellen, die ein NSDictionary mit NSString-Schlüsseln für Blockobjekte umschließt und dann einen weiteren Block für Standardfälle bereitstellt. Sie können sogar die tiefgestellte Notation unterstützen lassen.
ArtOfWarfare

1
Zusätzliche Punkte, wenn Sie eine NSDictionary-Unterklasse nur dafür
erstellen

2
Unter der Haube ist dies die Art und Weise, wie C # dies für große switch-Anweisungen tut.
Hank Schultz

77

Für mich ein schöner einfacher Weg:

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

1
Ich mag das. Es erfüllt die Anforderungen der meisten, die nach einer Antwort auf dieses Problem suchen, es erfordert nicht viel mehr Eingabe als ein ähnlicher Schalter in Javascript und es ist für Menschen lesbar.
ew parris

4
Ich würde diesen Hack nicht mit JS Switch vergleichen. Was passiert, wenn der nächste Programmierer ein Element zwischen Element1 und Element2 hinzufügt? Zu viel Potenzial für die Einführung von Fehlern
Aras

Es ist ein schöner Hack, also gebe ich dir Daumen hoch für die Mühe :)
Aras

@Aras Wenn der nächste Programmierer einen neuen Eintrag hinzufügen muss, fügt er ihn am Ende des Arrays mit einer neuen case-Anweisung am Ende hinzu, um ihn zu verarbeiten. Also kann @ "item0" nach @ "item3" im Array hinzugefügt werden, und dann einen Fall 3 hinzufügen: um damit umzugehen.
Sbonkosky

1
Ich mag deinen Weg total. Es ist sehr ordentlich. Ich schreibe eine Kategorie und muss UIColor zurückgeben, während ich einen String bei mir habe.
Alix

11

Leider können switch-Anweisungen nur für primitive Typen verwendet werden. Sie haben jedoch einige Optionen für die Verwendung von Sammlungen.

Die wahrscheinlich beste Option wäre, jeden Wert als Eintrag in einem NSDictionary zu speichern.

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

8

Ein bisschen spät, aber für jeden in der Zukunft konnte ich das für mich zum Laufen bringen

#define CASE(str) if ([__s__ isEqualToString:(str)])
#define SWITCH(s) for (NSString *__s__ = (s); ; )
#define DEFAULT

Das ist interessant. Können Sie mehr ausarbeiten?
Chen Li Yong

6

Hier ist die intelligentere Art, das zu schreiben. Es ist ein NSNumberFormatterim "Zauberstil" zu verwenden :

NSString *cardName = ...;

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

Beachten Sie, dass der Zahlenformatierer möchte, dass die Zeichenfolge in Kleinbuchstaben geschrieben wird. Daher müssen wir dies selbst tun, bevor wir sie an den Formatierer übergeben.


5

Es gibt andere Möglichkeiten, dies zu tun, aber switchkeine davon.

Wenn Sie wie in Ihrem Beispiel nur wenige Zeichenfolgen haben, ist der Code, den Sie haben, in Ordnung. Wenn Sie viele Fälle haben, können Sie die Zeichenfolgen als Schlüssel in einem Wörterbuch speichern und den entsprechenden Wert nachschlagen:

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

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

4

Bei weitem .. mein Favorit "ObjC Add-On" istObjectMatcher

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

UND es funktioniert auch mit Nicht-Strings ... sogar in Loops!

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.

Das Beste von allem, es gibt so wenige {...}‚s, :‘ s, und ()‚s


3

Objective-c unterscheidet sich in diesem Aspekt nicht von c, es kann nur einschalten, was c kann (und die Preproc-Defs wie NSInteger, NSUInteger, da sie letztendlich nur zu einem integralen Typ typisiert werden).

Wikipedia:

c-Syntax :

Die switch-Anweisung bewirkt, dass die Steuerung abhängig vom Wert eines Ausdrucks, der einen ganzzahligen Typ haben muss, auf eine von mehreren Anweisungen übertragen wird .

Integrale Typen :

In der Informatik ist eine Ganzzahl ein Datum vom integralen Datentyp, ein Datentyp, der eine endliche Teilmenge der mathematischen Ganzzahlen darstellt. Integrale Datentypen können unterschiedliche Größen haben und dürfen möglicherweise negative Werte enthalten oder nicht.


2

Ich bin etwas spät zur Party, aber um die Frage wie angegeben zu beantworten , gibt es einen intelligenteren Weg:

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

Beachten Sie, dass indexOfObjectdie Übereinstimmung mit isEqual:genau wie in der Frage gesucht wird.


2

Aufbauend auf der bereits veröffentlichten Idee von @Graham Perks wurde eine einfache Klasse entwickelt, um das Einschalten von Zeichenfolgen recht einfach und sauber zu gestalten.

@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

Sie würden es so verwenden:

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

Der richtige Block wird entsprechend der Zeichenfolge ausgeführt.

Kern für diese Lösung


0

Ich kann Cris Antwort auf @ Cris Antwort nicht kommentieren, aber ich möchte das sagen:

Es gibt eine EINSCHRÄNKUNG für die Methode von @ cris:

typedef enum akzeptiert keine alphanumerischen Werte

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

} CardType;

Also hier ist noch einer:

Link Stack over flow Gehe zu dieser Benutzerantwort "user1717750"


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

Viel Spaß beim Codieren .....

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.