在 Objective-C 中使用字符串实现可读 switch case 的最佳方法是什么?

发布于 2024-11-09 04:06:34 字数 3180 浏览 2 评论 0原文

在其他动态语言中,如 ruby​​、javascript 等,您可以简单地执行以下操作:

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

在 Objective-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。但是如何才能最小化这个简单任务的对象分配呢?有人对此有“更好的做法”吗?

编辑: 下面的代码是我想做的,但是获取枚举或#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:在“真实世界”中,我们需要使用一些加密哈希函数,但现在我使用 Arash Partow 的算法来保持示例简单。

所以,我的最后一个问题:有没有办法用预处理器以某种方式评估这些值?我想不会,但也许吧? :-)

类似于:

// !!!!!! 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>仅有的。 广义常量表达式 可能可以做到这一点为我。但我还在测试.​​..

In other dynamic languages like ruby, javascript etc. you can do simply this:

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

In objective-c, because it's derived very colsely from c language, you can't do that. My best practice for this is:

#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

It's seems to be ok, but #define makes foo and bar like a reserved word, and I can't use in my code. If I replace define constants with class constants, this problem is fixed, because in other classes I must use MyClassName before the constant name. But how can I minimize the object allocation for this simple task? Someone have a "better practice" for this?

EDIT:
The code below is what I wanted to do, but it's a little bit unconfortable to get the values of the enum or #define. Because I created an application what have just an input where I can write the string to get that hash and go back to xcode and set the values for the enums. So my problem is I can't do that in runtime time, because of the main behavour of switch case statement... Or if I do that with that NSDictionary way -> its have a lot of overhead compared with this solution.

#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 

NOTE: the hash function can defined elsewhere in common namespace to use it anywhere, typically I create a Utils.h or Common.h for this kind of stuff.

NOTE2: In "real word" we need to use some cryptographic hashing function, but now I used the algorithm by Arash Partow to keep the example simple.

So, my final question: Is there a way to evaluate these values with the preprocessor somehow? I think no, but maybe? :-)

Something like:

// !!!!!! 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: I found a "maybe solution" but it seems to be work with g++ 4.6> only. generalized constant expressions may be do it for me. But I'm still testing...

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

半衾梦 2024-11-16 04:06:35

该技术是从生产代码中提取的,并经过修改以在本示例中使用颜色。前提是字符串带有来自外部源的颜色文本名称。此入站颜色名称与系统中已知的 Crayola 颜色名称相匹配。如果新颜色名称与任何已知的 Crayola 颜色名称字符串匹配,则返回与该 Crayola 颜色名称等效的 HTML 十六进制代码的数值。

首先使用 http://www.unit-conversion.info/texttools/crc/ 将所有已知的 Crayola 颜色名称放入其中,以获得对应的数字。这些将在 case 语句中使用。然后将这些值放入枚举中以确保清洁度(例如下面的 LivingColors)。这些数字等同于实际的颜色名称字符串。

然后在运行时,变量文本通过相同的函数(但在代码内部)生成相同类型的数字常量。如果代码中的数字常量与静态生成的常量匹配,则它们表示的文本字符串完全相同。

内部代码函数是 crc32(),位于 zlib.h 中。这会根据输入的文本生成一个唯一的编号,就像上面的网页转换器一样。然后,可以在通用 C switch() 语句中使用来自 crc32() 的唯一数字,以与已预处理为枚举数字的已知颜色进行匹配。

要使用本机系统函数 crc32() 生成 CRC32B 值,请在项目中包含 /usr/lib/libz.1.dylib 进行链接。请务必在引用 crc32() 的源代码中包含或 #import

NSString 上实现 Objective C 类别使原生 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 类别来将两个方法添加到现有的 Cocoa 类 NSString 中,而不是对其进行子类化。

最终结果是,NSString 对象似乎能够本机获取自身的 CRC32B 值(在本示例之外非常方便),并且本质上可以 switch()对可能来自用户、文本文件等的颜色名称字符串进行识别,比任何文本字符串比较更快地识别匹配。

这种方法快速、高效且可靠,可以轻松适应与静态已知值文本匹配的任何类型的可变文本。请记住,CRC32B 校验和是通过按位运算生成的,而 C switch 语句使用在编译时优化的按位运算。请记住,这速度很快,因为 CRC32B 是高度优化的函数,用于检查 Mac/iPhone/iPad 接收的每个以太网数据包...即使您下载了 macOS Sierra 等多 GB 文件。

The technique is extracted from production code and modified to use colors for this example. The premise is that a string comes in with the text name of a color from some external feed. This inbound color name is matched against known Crayola color names in the system. If the new color name matches any known Crayola color name strings, the numeric value for HTML hex code equivalent of that Crayola color name is returned.

First use http://www.unit-conversion.info/texttools/crc/ and put all of your known Crayola color names through it to get numerical equivalents. These will be used in the case statements. Then put those values into an enumerated for cleanliness (e.g. LivingColors below). These numbers become equivalent to the actual color name string.

Then at run time the variable text is put through the same function, but internal to your code, to generate the same kind of numeric constant. If the numeric constant from the code matches the statically generated constant, then the text strings that they represent are exactly equal.

The internal code function is crc32() found in zlib.h. This generates a unique number based upon the text put through it just like the web page converter above. The unique number from crc32() can then be used in a common C switch() statement to match against the known colors which were pre-processed into numbers into the enumerated.

To use the native system function crc32() to generate CRC32B values, include the /usr/lib/libz.1.dylib in your project for linking. Be sure to include or #import <zlib.h> in your source that references crc32()

Implement an Objective C category on NSString to make the native NSString class understand the crc32: and htmlColor: messages.

Finally, read/get the name of the color into an NSString object, then send the string the htmlColor: message, it switches to match the 'strings' and returns the HTML hex equivalent value for a Crayola color name.

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

For the example an Objective C Category was made to add the two methods the existing Cocoa class NSString, rather than subclass it.

The end result is that an NSString object appears to have the ability to natively get a CRC32B value of itself (very handy beyond this example) and can essentially switch() on the color’s name string that possibly came in from the user, a text file, etc. to identify a match much faster than any text string comparison can occur.

Fast, efficient, and reliable, this approach can easily be adapted to any kind of variable text matching to static known value text. Bear in mind that CRC32B checksums are generated by bitwise operations and the C switch statement use bitwise operations optimized at compile time. Remember this is speedy because CRC32B is the highly optimized function used to check each ethernet packet your Mac/iPhone/iPad receives... even when you download multi-gigabyte files like macOS Sierra.

街角卖回忆 2024-11-16 04:06:34
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;
    }
}
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;
    }
}
醉态萌生 2024-11-16 04:06:34
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;
}
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;
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文