Лучший способ реализовать читаемый регистр с строками в 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.