лучший способ реализовать читаемый случай переключения со строками в target-c? - PullRequest
15 голосов
/ 22 мая 2011

В других динамических языках, таких как ruby, javascript и т. Д., Вы можете сделать это просто:

switch(someString) {
    case "foo":
       //do something;
       break;
    case "bar":
       // do something else;
       break;
    default:
       // do something by default;
}

В target-c, потому что он очень явно получен из языка c, вы не можете этого сделать. Моя лучшая практика для этого:

#import "CaseDemo.h"

#define foo 1
#define bar 2

static NSMutableDictionary * cases;

@implementation CaseDemo

- (id)init 
{
    self = [super init];
    if (self != nil) {
        if (cases == nil) {
            // this dict can be defined as a class variable
            cases = [[NSMutableDictionary alloc] initWithCapacity:2];
            [cases setObject:[NSNumber numberWithInt:foo] forKey:@"foo"];
            [cases setObject:[NSNumber numberWithInt:bar] forKey:@"bar"];
        }
    }
    return self;
}

- (void) switchFooBar:(NSString *) param {
    switch([[cases objectForKey:param] intValue]) {
        case foo:
            NSLog(@"its foo");
            break;
        case bar:
            NSLog(@"its bar");
            break;
        default: 
            NSLog(@"its default");
            break;
    }
}
@end

Кажется, все в порядке, но #define делает foo и bar похожими на зарезервированные слова, и я не могу использовать их в своем коде. Если я заменю определяющие константы константами классов, эта проблема будет исправлена, потому что в других классах я должен использовать MyClassName перед именем константы. Но как я могу минимизировать распределение объектов для этой простой задачи? У кого-то есть «лучшая практика» для этого?

EDIT: Код ниже - то, что я хотел сделать, но немного неудобно получать значения enum или #define. Потому что я создал приложение, которое имеет только вход, где я могу написать строку, чтобы получить этот хэш и вернуться к xcode и установить значения для перечислений. Так что моя проблема в том, что я не могу сделать это во время выполнения из-за основного поведения оператора switch case ... Или, если я делаю это с этим способом NSDictionary -> у него много накладных расходов по сравнению с этим решением.

#import "CaseDemo.h"

typedef enum {
    foo = 1033772579,
    bar = -907719821
} FooBar;

unsigned int APHash(NSString* s)
{
    const char* str = [s UTF8String];
    unsigned int len = [s length];    

    unsigned int hash = 0xAAAAAAAA;
    unsigned int i    = 0;

    for(i = 0; i < len; str++, i++)
    {
        hash ^= ((i & 1) == 0) ? (  (hash <<  7) ^ (*str) * (hash >> 3)) :
        (~((hash << 11) + ((*str) ^ (hash >> 5))));
    }

    return hash;
}

@implementation CaseDemo


- (void) switchFooBar:(NSString *) param {
    switch(APHash(param)) {
        case foo:
            NSLog(@"its foo");
            break;
        case bar:
            NSLog(@"its bar");
            break;
        default: 
            NSLog(@"its default");
            break;

    }
}

@end 

ПРИМЕЧАНИЕ: хеш-функция может быть определена в другом месте в общем пространстве имен, чтобы использовать ее где угодно, обычно я создаю Utils.h или Common.h для такого рода вещей.

ПРИМЕЧАНИЕ2: В «реальном слове» нам нужно использовать некоторую криптографическую функцию хеширования, но теперь я использовал алгоритм Араша Партоу, чтобы упростить пример.

Итак, мой последний вопрос: Есть ли способ как-то оценить эти значения с помощью препроцессора? Я думаю нет, но может быть? : -)

Что-то вроде:

// !!!!!! I know this code is not working, I don't want comments about "this is wrong" !!!!
// I want a solution to invoke method with preprocessor, or something like that. 
typedef enum {
        foo = APHash(@"foo"),
        bar = APHash(@"bar")
    } FooBar;

ОБНОВЛЕНИЕ : Я нашел «возможно решение», но, похоже, оно работает только с g ++ 4.6>. обобщенные константные выражения может быть сделать это для меня. Но я все еще тестирую ...

Ответы [ 3 ]

7 голосов
/ 22 мая 2011
typedef enum {
    foo,
    bar
} FooBar;

- (void) switchFooBar:(NSString *) param {
    switch([[cases objectForKey:param] intValue]) {
        case foo:
            NSLog(@"its foo");
            break;
        case bar:
            NSLog(@"its bar");
            break;
        default: 
            NSLog(@"its default");
            break;
    }
}
1 голос
/ 06 сентября 2013
NSString * extension = [fileName pathExtension];
NSString * directory = nil;

NSUInteger index = [@[@"txt",@"png",@"caf",@"mp4"] indexOfObjectPassingTest:^
                    BOOL(id obj, NSUInteger idx, BOOL *stop)
{
    return [obj isEqualToString:extension];
}];

switch (index)
{
    case 0:
        directory = @"texts/";
        break;
    case 1:
        directory = @"images/";
        break;
    case 2:
        directory = @"sounds/";
        break;
    case 3:
        directory = @"videos/";
        break;
    default:
        @throw [NSException exceptionWithName:@"unkonwnFileFormat"
                                       reason:[NSString stringWithFormat:@"zip file contain nknown file format: %@",fileName]
                                     userInfo:nil];
        break;
}
0 голосов
/ 05 марта 2017

Метод извлечен из производственного кода и изменен для использования цветов для этого примера.Предполагается, что в строку входит текстовое имя цвета из какого-либо внешнего источника.Это имя входящего цвета сопоставляется с известными именами цветов Crayola в системе.Если новое имя цвета совпадает с какими-либо известными строками имени цвета Crayola, возвращается числовое значение для шестнадцатеричного кода HTML, эквивалентное этому имени цвета Crayola.

Первое использование http://www.unit -conversion.info / texttools /crc / и введите все известные названия цветов Crayola, чтобы получить числовые эквиваленты.Они будут использованы в заявлениях по делуЗатем поместите эти значения в перечисленные для чистоты (например, LivingColors ниже).Эти числа становятся эквивалентными фактической строке имени цвета.

Затем во время выполнения текст переменной передается через ту же функцию, но внутреннюю для вашего кода, для генерации числовой константы того же типа.Если числовая константа из кода соответствует статически сгенерированной константе, то представляемые ими текстовые строки в точности равны.

Функция внутреннего кода crc32() найдена в zlib.h.Это генерирует уникальное число на основе текста, пропущенного через него, как конвертер веб-страницы выше.Уникальный номер из crc32() может затем использоваться в общем операторе C switch() для сопоставления с известными цветами, которые были предварительно обработаны в числа в перечисляемом.

Чтобы использовать встроенную системную функцию crc32() для генерации значений CRC32B, включите /usr/lib/libz.1.dylib в свой проект для компоновки.Не забудьте включить или #import <zlib.h> в ваш источник, который ссылается на crc32()

Реализовать категорию Objective C для NSString, чтобы родной класс NSString понимал сообщения crc32: и htmlColor:.

Наконец, прочитайте / получите название цвета в объекте NSString, затем отправьте строку в сообщение htmlColor:, она переключается в соответствии со «строками» и возвращает шестнадцатеричное эквивалентное значение HTML дляИмя цвета Crayola.

#import <zlib.h>

#define typedefEnum( enumName )               typedef enum enumName enumName; enum enumName

/**
    @see Crayola Web Colors https://www.w3schools.com/colors/colors_crayola.asp
    @see CRC32B value generator for static case strings http://www.unit-conversion.info/texttools/crc/ or http://www.md5calc.com
*/

#define typedefEnum( enumName ) typedef enum enumName enumName; enum enumName

typedefEnum( LivingColors ) {
    kRedColor                   = 0xc22c196f,    // "Red" is 0xED0A3F in HTML
    kBlueberryColor             = 0xfbefa670,    // "Blueberry" is 0x4F86F7 in HTML
    kLightChromeGreenColor      = 0x44b77242,    // "Light Chrome Green" is 0xBEE64B in HTML
    kPermanentGeraniumLakeColor = 0xecc4f3e4,    // "Permanent Geranium Lake" is 0xE12C2C in HTML
    kIlluminatingEmeraldColor   = 0x4828d5f2,    // "Illuminating Emerald" is 0x319177 in HTML
    kWildWatermelonColor        = 0x1a17c629,    // "Wild Watermelon" is 0xFD5B78 in HTML
    kWashTheDogColor            = 0xea9fcbe6,    // "Wash the Dog" is 0xFED85D in HTML
    kNilColor                   = 0xDEADBEEF     // could use '0' but what fun is that?
};


// generates the CRC32B, same used to check each ethernet packet on the network you receive so it’s fast
- (NSUInteger) crc32 {
    NSUInteger theResult;

    theResult = (NSUInteger)crc32(  0L,
                                    (const unsigned char *) [self UTF8String],
                                    (short)self.length);

    return theResult;
}

/// @return the HTML hex value for a recognized color name string.
- (NSUInteger) htmlColor {
    NSUInteger      theResult               = 0x0;
    LivingColors    theColorInLivingColor   = kNilColor;

    theColorInLivingColor = (LivingColors) [self crc32];
     // return the HTML value for a known color by effectively switching on a string.
    switch ( theColorInLivingColor ) {

        case kRedColor : {
            theResult = 0xED0A3F;
        }
            break;

        case kBlueberryColor : {
            theResult = 0x4F86F7;
        }
            break;

        case kLightChromeGreenColor : {
            theResult = 0xBEE64B;
        }
            break;

        case kPermanentGeraniumLakeColor : {
            theResult = 0xE12C2C;
        }
            break;

        case kIlluminatingEmeraldColor : {
            theResult = 0x319177;
        }
            break;

        case kWildWatermelonColor : {
            theResult = 0xFD5B78;
        }
            break;

        case kWashTheDogColor : {
            theResult = 0xFED85D;
        }
            break;

        case kNilColor :
        default : {
            theResult = 0x0;
        }
            break;

    }

    return theResult;
}

Для примера была создана категория Objective C для добавления двух методов к существующему классу Какао NSString, а не к его подклассу.

Конечным результатом является то, что NSString объект появляется , чтобы иметь возможность изначально получить значение CRC32B (очень удобно, кроме этого примера) и может по существу switch() настрока имени цвета, которая, возможно, поступила от пользователя, текстовый файл и т. д., чтобы идентифицировать совпадение намного быстрее, чем может произойти любое сравнение текстовой строки.

Быстрый, эффективный и надежный, этот подход легко может бытьадаптирован к любому типу переменного текста, соответствующего статическому тексту с известным значением.Помните, что контрольные суммы CRC32B генерируются побитовыми операциями, а оператор C switch использует побитовые операции, оптимизированные во время компиляции.Помните, что это быстро, потому что CRC32B - это высоко оптимизированная функция, используемая для проверки каждого пакета Ethernet, который получает ваш Mac / iPhone / iPad ... даже при загрузке файлов объемом в несколько гигабайт, таких как macOS Sierra.

...