Лучший способ реализовать читаемый регистр с строками в objective-c?

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

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

В objective-c, потому что он вывел очень colsely из 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 и установить значения для перечислений. Поэтому моя проблема заключается в том, что я не могу этого сделать во время выполнения из-за основного поведения оператора case switch. Или если я делаю это с помощью этого пути 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;

UPDATE. Я нашел "возможно решение", но, похоже, работает только с g++ 4.6 > . обобщенные константные выражения могут сделать это для меня. Но я все еще тестирую...

Ответы

Ответ 1

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

Ответ 2

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

Ответ 3

Этот метод извлекается из производственного кода и модифицируется для использования цветов для этого примера. Предпосылка заключается в том, что строка содержит текстовое имя цвета из некоторого внешнего фида. Это имя входящего цвета сопоставляется с известными именами цветов 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, чтобы класс native 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 Category для добавления двух методов существующего класса Cocoa NSString, а не подкласса.

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

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