แปลงเป้าหมาย -c typedef ให้เทียบเท่ากับสตริง


141

สมมติว่าฉันมี typedef ประกาศในไฟล์. h ของฉันเช่น:

typedef enum {
  JSON,
  XML,
  Atom,
  RSS
} FormatType;

ฉันต้องการสร้างฟังก์ชั่นที่แปลงค่าตัวเลขของ typedef เป็นสตริง ตัวอย่างเช่นหากข้อความ[self toString:JSON]ถูกส่ง มันจะส่งคืน 'JSON'

ฟังก์ชันจะมีลักษณะดังนี้:

-(NSString *) toString:(FormatType)formatType {
  //need help here
  return [];
}

บังเอิญถ้าฉันลองไวยากรณ์นี้

[self toString:FormatType.JSON];

เพื่อส่งค่า typedef ไปยังเมธอดฉันได้รับข้อผิดพลาด ฉันพลาดอะไรไป


ดูคำตอบของฉันได้ที่stackoverflow.com/questions/6331762/enum-values-to-nsstring-iosสำหรับวิธีแก้ไขปัญหาที่สะอาดกว่า
BooTooMany

4
บางทีเราควรให้กอดกับภาษาสวิฟต์ใน enum
Itachi

@craig: นี่คือทางออกgithub.com/ndpiparava/ObjcEnumString
Nitin

สำหรับวิธีการแก้ปัญหาโดยไม่ต้องใช้ getter แบบกำหนดเองสำหรับ enum ที่แปลงเป็นสตริงให้ตรวจสอบมาโครอาร์เรย์แบบสตริง - สแต็ค: stackoverflow.com/a/53755377/2057171
Albert Renshaw

คำตอบ:


135

นี่เป็นคำถาม C จริงๆไม่ใช่เฉพาะกับ Objective-C (ซึ่งเป็นภาษาซีของภาษา C) Enums ใน C แสดงเป็นจำนวนเต็ม ดังนั้นคุณต้องเขียนฟังก์ชั่นที่คืนค่าสตริงที่กำหนดค่า enum มีหลายวิธีในการทำเช่นนี้ อาร์เรย์ของสตริงเช่นที่ค่า enum สามารถใช้เป็นดัชนีในอาร์เรย์หรือโครงสร้างแผนที่ (เช่น an NSDictionary) ที่แมปค่า enum กับการทำงานของสตริง แต่ฉันพบว่าวิธีการเหล่านี้ไม่ชัดเจนเท่ากับฟังก์ชันที่ ทำให้เกิดการแปลงอย่างชัดเจน (และวิธีการแบบอาร์เรย์แม้ว่าCวิธีแบบคลาสสิคจะเป็นอันตรายหากค่า enum ของคุณไม่ต่อเนื่องจาก 0) สิ่งนี้จะทำงาน:

- (NSString*)formatTypeToString:(FormatType)formatType {
    NSString *result = nil;

    switch(formatType) {
        case JSON:
            result = @"JSON";
            break;
        case XML:
            result = @"XML";
            break;
        case Atom:
            result = @"Atom";
            break;
        case RSS:
            result = @"RSS";
            break;
        default:
            [NSException raise:NSGenericException format:@"Unexpected FormatType."];
    }

    return result;
}

คำถามที่เกี่ยวข้องของคุณเกี่ยวกับไวยากรณ์ที่ถูกต้องสำหรับค่า enum คือคุณใช้เฉพาะค่า (เช่นJSON) ไม่ใช่FormatType.JSONไวยากรณ์ FormatTypeเป็นชนิดและค่า enum (เช่นJSON, XMLฯลฯ ) เป็นค่าที่คุณสามารถกำหนดให้กับประเภทนั้น


127

คุณทำมันไม่ได้ง่ายๆ ใน C และ Objective-C, enums เป็นค่าคงที่จำนวนเต็มที่ได้รับการยกย่องจริงๆ คุณจะต้องสร้างตารางชื่อด้วยตัวคุณเอง (หรือมีการละเมิดผู้ประมวลผลล่วงหน้า) ตัวอย่างเช่น:

// In a header file
typedef enum FormatType {
    JSON,
    XML,
    Atom,
    RSS
} FormatType;

extern NSString * const FormatType_toString[];

// In a source file
// initialize arrays with explicit indices to make sure 
// the string match the enums properly
NSString * const FormatType_toString[] = {
    [JSON] = @"JSON",
    [XML] = @"XML",
    [Atom] = @"Atom",
    [RSS] = @"RSS"
};
...
// To convert enum to string:
NSString *str = FormatType_toString[theEnumValue];

อันตรายของวิธีนี้คือถ้าคุณเปลี่ยน enum คุณต้องจำไว้ว่าให้เปลี่ยนชื่ออาร์เรย์ คุณสามารถแก้ปัญหานี้ได้ด้วยการละเมิดของผู้ประมวลผลก่อน แต่ก็ยุ่งยากและน่าเกลียด

นอกจากนี้โปรดทราบว่าสิ่งนี้ถือว่าคุณมีค่าคงที่ enum ที่ถูกต้อง หากคุณมีค่าจำนวนเต็มจากแหล่งที่ไม่น่าเชื่อถือคุณยังจำเป็นที่จะต้องทำการตรวจสอบว่าค่าคงที่ของคุณถูกต้องเช่นโดยรวม "แม็กซ์ที่ผ่านมา" ใน enum sizeof(FormatType_toString) / sizeof(FormatType_toString[0])ของคุณหรือโดยการตรวจสอบถ้ามันน้อยกว่าความยาวอาร์เรย์


37
คุณสามารถเริ่มต้นกับอาร์เรย์ดัชนีอย่างชัดเจนเช่นstring[] = { [XML] = "XML" }เพื่อให้แน่ใจว่าสายตรง enums ถูกต้อง
คริสโต

@Christoph: ใช่ว่าเป็นคุณสมบัติที่เรียกว่า C99 initializers กำหนด ไม่เป็นไรที่จะใช้ใน Objective-C (ซึ่งอิงจาก C99) แต่สำหรับรหัส C89 ทั่วไปคุณไม่สามารถใช้สิ่งเหล่านั้นได้
Adam Rosenfield

มีวิธีอื่นอีกไหม? ตัวอย่างเช่นรับค่า enum กลับมาเป็นสตริง?
Jameo

1
@Jameo: ใช่ แต่มันไม่ง่ายเหมือนการค้นหาอาร์เรย์ คุณจะต้องวนซ้ำผ่านFormatType_toString[]อาร์เรย์และเรียก-isEqualToString:แต่ละองค์ประกอบเพื่อค้นหาการจับคู่หรือใช้ชนิดข้อมูลการแมปเช่นNSDictionaryเพื่อรักษาแผนที่การค้นหาแบบผกผัน
Adam Rosenfield

1
เคล็ดลับของMax Oนั้นดีเกี่ยวกับการลืมเพิ่มรายการในFormatType_toStringอาร์เรย์
AechoLiu

50

โซลูชันของฉัน:

แก้ไข: ฉันได้เพิ่มโซลูชันที่ดีขึ้นในตอนท้ายโดยใช้ Modern Obj-C

1.
ใส่ชื่อเป็นคีย์ในอาร์เรย์
ตรวจสอบให้แน่ใจว่าดัชนีเป็น enums ที่เหมาะสมและในลำดับที่ถูกต้อง (ยกเว้นอย่างอื่น)
หมายเหตุ: ชื่อเป็นคุณสมบัติที่ถูกสังเคราะห์เป็น * _names *;

รหัสไม่ได้รับการตรวจสอบเพื่อรวบรวม แต่ฉันใช้เทคนิคเดียวกันในแอปของฉัน

typedef enum {
  JSON,
  XML,
  Atom,
  RSS
} FormatType;

+ (NSArray *)names
{
    static NSMutableArray * _names = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        _names = [NSMutableArray arrayWithCapacity:4];
        [_names insertObject:@"JSON" atIndex:JSON];
        [_names insertObject:@"XML" atIndex:XML];
        [_names insertObject:@"Atom" atIndex:Atom];
        [_names insertObject:@"RSS" atIndex:RSS];
    });

    return _names;
}

+ (NSString *)nameForType:(FormatType)type
{
    return [[self names] objectAtIndex:type];
}


//

2.
การใช้ Modern Obj-C คุณสามารถใช้พจนานุกรมเพื่อผูกคำอธิบายกับคีย์ใน enum สั่งซื้อสินค้าที่ไม่ได้เรื่อง


typedef NS_ENUM(NSUInteger, UserType) {
    UserTypeParent = 0,
    UserTypeStudent = 1,
    UserTypeTutor = 2,
    UserTypeUnknown = NSUIntegerMax
};  

@property (nonatomic) UserType type;

+ (NSDictionary *)typeDisplayNames
{
    return @{@(UserTypeParent) : @"Parent",
             @(UserTypeStudent) : @"Student",
             @(UserTypeTutor) : @"Tutor",
             @(UserTypeUnknown) : @"Unknown"};
}

- (NSString *)typeDisplayName
{
    return [[self class] typeDisplayNames][@(self.type)];
}


การใช้งาน (ในวิธีอินสแตนซ์ของคลาส):

NSLog(@"%@", [self typeDisplayName]);



12
โปรดทราบว่าทุกครั้งที่คุณโทร+[typeDisplayNames]คุณจะสร้างพจนานุกรมขึ้นใหม่ นี่เป็นเรื่องปกติถ้ามันถูกเรียกเพียงไม่กี่ครั้ง แต่ถ้ามันถูกเรียกหลายครั้งสิ่งนี้จะมีราคาแพงมาก ทางออกที่ดีกว่าอาจจะทำให้พจนานุกรมเป็นซิงเกิลดังนั้นมันจึงสร้างเพียงครั้งเดียวและอยู่ในหน่วยความจำอย่างอื่น หน่วยความจำแบบคลาสสิกกับข้อ จำกัด ของ CPU
Joel Fischer

หรือเปลี่ยนเป็นตัวแปรแบบคงที่เช่นstatic NSDictionary *dict = nil; if(!dict) dict = @{@(UserTypeParent): @"Parent"}; return dict;ความคิดเห็นจะไม่ยอมให้คุณแบ่งบรรทัดขออภัยสำหรับสิ่งนั้น
natanavra

29

การรวม @AdamRosenfield คำตอบความคิดเห็น @Christoph และเคล็ดลับในการจัดการกับ C ธรรมดาอีกครั้งฉันขอแนะนำ:

// In a header file
typedef enum {
  JSON = 0,         // explicitly indicate starting index
  XML,
  Atom,
  RSS,

  FormatTypeCount,  // keep track of the enum size automatically
} FormatType;
extern NSString *const FormatTypeName[FormatTypeCount];


// In a source file
NSString *const FormatTypeName[FormatTypeCount] = {
  [JSON] = @"JSON",
  [XML] = @"XML",
  [Atom] = @"Atom",
  [RSS] = @"RSS",
};


// Usage
NSLog(@"%@", FormatTypeName[XML]);

ในกรณีที่แย่ที่สุด - เช่นถ้าคุณเปลี่ยน enum แต่ลืมเปลี่ยนชื่อ array มันจะคืนค่าศูนย์สำหรับคีย์นี้


12

กำหนด typedef enum ในส่วนหัวของชั้นเรียน:

typedef enum {
    IngredientType_text  = 0,
    IngredientType_audio = 1,
    IngredientType_video = 2,
    IngredientType_image = 3
} IngredientType;

เขียนวิธีการเช่นนี้ในชั้นเรียน:

+ (NSString*)typeStringForType:(IngredientType)_type {
   NSString *key = [NSString stringWithFormat:@"IngredientType_%i", _type];
   return NSLocalizedString(key, nil);
}

มีสตริงภายในไฟล์Localizable.strings :

/* IngredientType_text */
"IngredientType_0" = "Text";
/* IngredientType_audio */
"IngredientType_1" = "Audio";
/* IngredientType_video */
"IngredientType_2" = "Video";
/* IngredientType_image */
"IngredientType_3" = "Image";

11

ฉันจะใช้ # string token ของคอมไพเลอร์ (พร้อมด้วยมาโครเพื่อทำให้กะทัดรัดยิ่งขึ้น):

#define ENUM_START              \
            NSString* ret;      \
            switch(value) {

#define ENUM_CASE(evalue)       \
            case evalue:        \
                ret = @#evalue; \
                break;

#define ENUM_END                \
            }                   \
            return ret;

NSString*
_CvtCBCentralManagerStateToString(CBCentralManagerState value)
{
    ENUM_START
        ENUM_CASE(CBCentralManagerStateUnknown)
        ENUM_CASE(CBCentralManagerStateResetting)
        ENUM_CASE(CBCentralManagerStateUnsupported)
        ENUM_CASE(CBCentralManagerStateUnauthorized)
        ENUM_CASE(CBCentralManagerStatePoweredOff)
        ENUM_CASE(CBCentralManagerStatePoweredOn)
    ENUM_END
}

สิ่งนี้ใช้งานได้ดีใน C99 - ฉันใหม่ที่ C และฉันพบว่านี่เป็นวิธีที่สะอาดที่สุดในการทำคำถามที่ถาม ฉันยังเพิ่มค่าเริ่มต้นในการใช้งานสำหรับรายการที่อาจไม่ได้กำหนดไว้ วิธีการที่สะอาดมาก ขอบคุณสำหรับผลลัพธ์ ใช้มาโครอย่างมีเล่ห์เหลี่ยมมาก
TravisWhidden

8

ฉันชอบ#defineวิธีการทำสิ่งนี้:

// วางสิ่งนี้ในไฟล์. h ของคุณนอกบล็อค @interface

typedef enum {
    JPG,
    PNG,
    GIF,
    PVR
} kImageType;
#define kImageTypeArray @"JPEG", @"PNG", @"GIF", @"PowerVR", nil

// Place this in the .m file, inside the @implementation block
// A method to convert an enum to string
-(NSString*) imageTypeEnumToString:(kImageType)enumVal
{
    NSArray *imageTypeArray = [[NSArray alloc] initWithObjects:kImageTypeArray];
    return [imageTypeArray objectAtIndex:enumVal];
}

แหล่งที่มา (ไม่มีแหล่งที่มาอีกต่อไป)


@ Daij-Djan แล้วnilถ้ากลับมาarray.count <= enumValueล่ะ
anneblue

@anneblue ที่จะจับข้อผิดพลาด .. มันจะงัวเพราะถ้าคุณเพิ่มค่า enum หรือค่าจำนวนเต็มของค่า enum การเปลี่ยนแปลงนี้จะผิดพลาด คำตอบที่ได้รับการยอมรับจะดี
Daij-Djan

@codercat :( เสียใจ - ไม่แน่ใจว่าสิ่งที่เกิดขึ้นไปยังเว็บไซต์ที่ไม่ได้อยู่ในทางกลับเมื่อเครื่องทั้ง ... .
Lindon สุนัขจิ้งจอก

ฉันมีคำถามเล็ก ๆ เกี่ยวกับคำตอบข้างต้น วิธีการแปลงองค์ประกอบสตริงเป็น kImageType ฉันจำเป็นต้องเรียกใช้เมธอด imageTypeEnumToString โดยการส่งผ่านสตริงคุณช่วยฉันด้วยสำหรับปัญหาของฉัน
พระพิฆเนศ

1
ฉันชอบคำตอบนี้ดีที่สุดเพราะคุณมีคำจำกัดความสตริงติดกับ enums โอกาสที่น้อยที่สุดที่จะพลาดค่า และ @Ganesh การแปลงจากค่าดิบสามารถทำได้ดังนี้ return (kImageType) [imageTypeArray indexOfObject: rawValue];
แฮร์ริส

8

ฉันได้ผสมผสานวิธีการแก้ปัญหาทั้งหมดที่พบในหน้านี้เพื่อสร้างของฉันมันเป็นส่วนขยายเชิงวัตถุหรือเชิงรุก

อันที่จริงถ้าคุณต้องการมากกว่าแค่ค่าคงที่ (เช่นจำนวนเต็ม) คุณอาจต้องการวัตถุจำลอง (เราทุกคนกำลังพูดถึง MVC ใช่ไหม?)

เพียงแค่ถามคำถามกับตัวเองก่อนที่จะใช้สิ่งนี้ใช่ไหมใช่ไหมที่จริงแล้วคุณจำเป็นต้องมีโมเดลวัตถุจริงเริ่มต้นจากเว็บเซอร์ Plist ฐานข้อมูล SQLite หรือ CoreData หรือไม่

อย่างไรก็ตามรหัสที่นี่ (MPI สำหรับ "ชื่อโครงการของฉัน" ทุกคนใช้ชื่อนี้หรือดูเหมือนว่า):

MyWonderfulType.h :

typedef NS_ENUM(NSUInteger, MPIMyWonderfulType) {
    MPIMyWonderfulTypeOne = 1,
    MPIMyWonderfulTypeTwo = 2,
    MPIMyWonderfulTypeGreen = 3,
    MPIMyWonderfulTypeYellow = 4,
    MPIMyWonderfulTypePumpkin = 5
};

#import <Foundation/Foundation.h>

@interface MyWonderfulType : NSObject

+ (NSString *)displayNameForWonderfulType:(MPIMyWonderfulType)wonderfulType;
+ (NSString *)urlForWonderfulType:(MPIMyWonderfulType)wonderfulType;

@end

และMyWonderfulType.m:

#import "MyWonderfulType.h"

@implementation MyWonderfulType

+ (NSDictionary *)myWonderfulTypeTitles
{
    return @{
             @(MPIMyWonderfulTypeOne) : @"One",
             @(MPIMyWonderfulTypeTwo) : @"Two",
             @(MPIMyWonderfulTypeGreen) : @"Green",
             @(MPIMyWonderfulTypeYellow) : @"Yellow",
             @(MPIMyWonderfulTypePumpkin) : @"Pumpkin"
             };
}

+ (NSDictionary *)myWonderfulTypeURLs
{
    return @{
             @(MPIMyWonderfulTypeOne) : @"http://www.theone.com",
             @(MPIMyWonderfulTypeTwo) : @"http://www.thetwo.com",
             @(MPIMyWonderfulTypeGreen) : @"http://www.thegreen.com",
             @(MPIMyWonderfulTypeYellow) : @"http://www.theyellow.com",
             @(MPIMyWonderfulTypePumpkin) : @"http://www.thepumpkin.com"
             };
}

+ (NSString *)displayNameForWonderfulType:(MPIMyWonderfulType)wonderfulType {
    return [MPIMyWonderfulType myWonderfulTypeTitles][@(wonderfulType)];
}

+ (NSString *)urlForWonderfulType:(MPIMyWonderfulType)wonderfulType {
    return [MPIMyWonderfulType myWonderfulTypeURLs][@(wonderfulType)];
}


@end

ดูดี แต่คุณกำลังจัดสรรและส่งคืนพจนานุกรมเต็มรูปแบบเมื่อคุณต้องการเพียงหนึ่งในค่าของมัน ประสิทธิภาพ VS โค้ดสวยหรือไม่? ขึ้นอยู่กับสิ่งที่คุณต้องการและคุณจะพอใจกับสิ่งนี้หากคุณไม่ได้ใช้มันในโค้ดของคุณเหมือนวงใหญ่ แต่นี่อาจจะเป็นประโยชน์กับ enums "ไดนามิก" หรือรหัสที่ไม่ใช่ฮาร์ดโค้ดที่มาจากเซิร์ฟเวอร์เช่น
user2387149

5

ทางออกอื่น:

typedef enum BollettinoMavRavTypes {
    AMZCartServiceOperationCreate,
    AMZCartServiceOperationAdd,
    AMZCartServiceOperationGet,
    AMZCartServiceOperationModify
} AMZCartServiceOperation;

#define AMZCartServiceOperationValue(operation) [[[NSArray alloc] initWithObjects: @"CartCreate", @"CartAdd", @"CartGet", @"CartModify", nil] objectAtIndex: operation];

ในวิธีการของคุณคุณสามารถใช้:

NSString *operationCheck = AMZCartServiceOperationValue(operation);

4

ปรับปรุง @ yar1vn คำตอบโดยวางการพึ่งพาสตริง:

#define VariableName(arg) (@""#arg)

typedef NS_ENUM(NSUInteger, UserType) {
    UserTypeParent = 0,
    UserTypeStudent = 1,
    UserTypeTutor = 2,
    UserTypeUnknown = NSUIntegerMax
};  

@property (nonatomic) UserType type;

+ (NSDictionary *)typeDisplayNames
{
    return @{@(UserTypeParent) : VariableName(UserTypeParent),
             @(UserTypeStudent) : VariableName(UserTypeStudent),
             @(UserTypeTutor) : VariableName(UserTypeTutor),
             @(UserTypeUnknown) : VariableName(UserTypeUnknown)};
}

- (NSString *)typeDisplayName
{
    return [[self class] typeDisplayNames][@(self.type)];
}

ดังนั้นเมื่อคุณจะเปลี่ยนชื่อรายการ enum สตริงที่เกี่ยวข้องจะมีการเปลี่ยนแปลง มีประโยชน์ในกรณีที่คุณจะไม่แสดงข้อความนี้ให้ผู้ใช้เห็น


คุณช่วยอธิบาย "- กำหนด VariableName (arg) (@" "# arg) --- และอาจให้ทางออกที่ดีกว่าได้ไหม
xySVerma

ด้วย #defines เมื่อคุณใช้ # สำหรับการทดแทนอาร์กิวเมนต์จะถูกล้อมด้วยเครื่องหมายคำพูดคู่โดยอัตโนมัติ ใน C เมื่อสองสายปรากฏขึ้นติดกันในรหัสเช่น"foo""bar"มันจะส่งผลให้เกิดสตริง"foobar"เมื่อรวบรวม ดังนั้น#define VariableName(arg) (@""#arg)จะขยายให้เป็นVariableName(MyEnum) ที่จะส่งผลในสตริง(@"""MyEnum") @"MyEnum"
Chris Douglass

3

กำหนดนิยาม enum เช่น:

typedef NS_ENUM(NSInteger, AssetIdentifier) {
    Isabella,
    William,
    Olivia
};

เราสามารถกำหนดแมโครเพื่อแปลงค่า enum เป็นสตริงที่สอดคล้องกันดังที่แสดงด้านล่าง

#define AssetIdentifier(asset) \
^(AssetIdentifier identifier) { \
switch (identifier) { \
case asset: \
default: \
return @#asset; \
} \
}(asset)

switchคำสั่งที่ใช้ในการบล็อกสำหรับการตรวจสอบชนิดและยังได้รับการสนับสนุนอัตโนมัติสมบูรณ์ใน Xcode

ป้อนคำอธิบายรูปภาพที่นี่ ป้อนคำอธิบายรูปภาพที่นี่


2

ฉันมีประเภทแจกแจงจำนวนมากที่ฉันต้องการแปลงเป็นNSDictionaryค้นหา ฉันสิ้นสุดการใช้sedจาก terminal OSX เป็น:

$ sed -E 's/^[[:space:]]{1,}([[:alnum:]]{1,}).*$/  @(\1) : @"\1",/g' ObservationType.h

ซึ่งสามารถอ่านได้เป็น: 'จับคำแรกบนบรรทัดและเอาท์พุท @ (คำ): @ "คำ",'

regex นี้จะแปลง enum ในไฟล์ส่วนหัวชื่อ 'ObservationType.h' ซึ่งมี:

typedef enum : int { 
    ObservationTypePulse = 1,
    ObservationTypeRespRate = 2,
    ObservationTypeTemperature = 3,
    .
    .
}

เป็นสิ่งที่ชอบ:

    @(ObservationTypePulse) : @"ObservationTypePulse",
    @(ObservationTypeRespRate) : @"ObservationTypeRespRate",
    @(ObservationTypeTemperature) : @"ObservationTypeTemperature",
    .
    .

ซึ่งสามารถห่อในวิธีการโดยใช้ไวยากรณ์วัตถุประสงค์ -c ที่ทันสมัย@{ }(ตามที่อธิบายโดย @ yar1vn ด้านบน) เพื่อสร้างการNSDictionaryค้นหา:

-(NSDictionary *)observationDictionary
{
    static NSDictionary *observationDictionary;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        observationDictionary = [[NSDictionary alloc] initWithDictionary:@{
                                 @(ObservationTypePulse) : @"ObservationTypePulse",
                                 @(ObservationTypeRespRate) : @"ObservationTypeRespRate",
                                 .
                                 .
                                 }];
    });
    return observationDictionary;
}

dispatch_onceหม้อไอน้ำจานเป็นเพียงเพื่อให้มั่นใจว่าตัวแปรคงที่จะเริ่มต้นใช้ในลักษณะด้ายปลอดภัย

หมายเหตุ: ฉันพบนิพจน์ sed regex บน OSX คี่ - เมื่อฉันพยายามใช้+เพื่อจับคู่ 'หนึ่งหรือมากกว่า' มันไม่ทำงานและต้องหันไปใช้{1,}แทน


2

ฉันใช้รูปแบบของคำตอบของ Barry Walk นั่นคือตามลำดับความสำคัญ:

  1. อนุญาตให้คอมไพเลอร์ตรวจสอบข้อกรณีที่ขาดหายไป (ไม่สามารถถ้าคุณมีส่วนคำสั่งเริ่มต้น)
  2. ใช้ชื่อทั่วไป Objective-C (แทนที่จะเป็น Java like name)
  3. เพิ่มข้อยกเว้นเฉพาะ
  4. จะสั้นกว่า

เช่น:

- (NSString*)describeFormatType:(FormatType)formatType {    
    switch(formatType) {
        case JSON:
            return @"JSON";
        case XML:
            return @"XML";
        case Atom:
            return @"Atom";
        case RSS:
            return @"RSS";
    }
    [NSException raise:NSInvalidArgumentException format:@"The given format type number, %ld, is not known.", formatType];
    return nil; // Keep the compiler happy - does not understand above line never returns!
}

2

@pixel เพิ่มคำตอบที่ยอดเยี่ยมที่สุดที่นี่: https://stackoverflow.com/a/24255387/1364257 โปรดโหวตขึ้น เขา!

เขาใช้มาโคร X ที่เรียบร้อยจากยุค 1960 (ฉันได้เปลี่ยนรหัสเล็กน้อยสำหรับ ObjC ที่ทันสมัย)

#define X(a, b, c) a b,
enum ZZObjectType {
    XXOBJECTTYPE_TABLE
};
typedef NSUInteger TPObjectType;
#undef X

#define XXOBJECTTYPE_TABLE \
X(ZZObjectTypeZero, = 0, @"ZZObjectTypeZero") \
X(ZZObjectTypeOne, , @"ZZObjectTypeOne") \
X(ZZObjectTypeTwo, , @"ZZObjectTypeTwo") \
X(ZZObjectTypeThree, , @"ZZObjectTypeThree")

+ (NSString*)nameForObjectType:(ZZObjectType)objectType {
#define X(a, b, c) @(a):c, 
    NSDictionary *dict = @{XXOBJECTTYPE_TABLE};
#undef X
    return dict[objectType];
}

แค่นั้นแหละ. สะอาดและเรียบร้อย ขอบคุณ @pixel! https://stackoverflow.com/users/21804/pixel


@ AlexandreG ให้บริการโซลูชั่นของคุณคน มันง่ายที่จะปลาคาร์พที่ใครบางคน วิธีนี้มีข้อดีชัดเจนและข้อเสียชัดเจน ทำให้โลกดีขึ้นด้วยโซลูชันของคุณ
voiger

2

ฉันรวมหลายวิธีเข้าด้วยกันที่นี่ ฉันชอบความคิดของผู้ประมวลผลล่วงหน้าและรายการที่จัดทำดัชนี

ไม่มีการจัดสรรแบบไดนามิกพิเศษและเนื่องจากการอินไลน์การคอมไพเลอร์อาจสามารถเพิ่มประสิทธิภาพการค้นหา

typedef NS_ENUM(NSUInteger, FormatType) { FormatTypeJSON = 0, FormatTypeXML, FormatTypeAtom, FormatTypeRSS, FormatTypeCount };

NS_INLINE NSString *FormatTypeToString(FormatType t) {
  if (t >= FormatTypeCount)
    return nil;

#define FormatTypeMapping(value) [value] = @#value

  NSString *table[FormatTypeCount] = {FormatTypeMapping(FormatTypeJSON),
                                      FormatTypeMapping(FormatTypeXML),
                                      FormatTypeMapping(FormatTypeAtom),
                                      FormatTypeMapping(FormatTypeRSS)};

#undef FormatTypeMapping

  return table[t];
}

1

ประการแรกเกี่ยวกับ FormatType.JSON: JSON ไม่ได้เป็นสมาชิกของ FormatType มันเป็นค่าที่เป็นไปได้ของประเภท FormatType ไม่ได้เป็นประเภทคอมโพสิต - มันเป็นเซนต์คิตส์และเนวิส

ประการที่สองวิธีเดียวในการทำเช่นนี้คือการสร้างตารางการแมป วิธีที่ใช้กันมากขึ้นในการทำเช่นนี้ใน Objective-C คือการสร้างชุดของค่าคงที่ที่กล่าวถึง "สัญลักษณ์" ของคุณเพื่อให้คุณจะต้องNSString *FormatTypeJSON = @"JSON"และอื่น ๆ


1

ต่อไปนี้มีวิธีแก้ปัญหาเช่นการเพิ่ม enum ใหม่ต้องใช้การแก้ไขเพียงบรรทัดเดียวงานที่คล้ายกันเพื่อเพิ่มบรรทัดเดียวในรายการ enum {}

//------------------------------------------------------------------------------
// enum to string example
#define FOR_EACH_GENDER(tbd) \
        tbd(GENDER_MALE) \
        tbd(GENDER_FEMALE) \
        tbd(GENDER_INTERSEX) \

#define ONE_GENDER_ENUM(name) name,
enum
{
    FOR_EACH_GENDER(ONE_GENDER_ENUM)
    MAX_GENDER
};

#define ONE_GENDER(name) #name,
static const char *enumGENDER_TO_STRING[] = 
{
    FOR_EACH_GENDER(ONE_GENDER)
};

// access string name with enumGENDER_TO_STRING[value]
// or, to be safe converting from a untrustworthy caller
static const char *enumGenderToString(unsigned int value)
{
    if (value < MAX_GENDER)
    {
        return enumGENDER_TO_STRING[value];
    }
    return NULL;
}

static void printAllGenders(void)
{
    for (int ii = 0;  ii < MAX_GENDER;  ii++)
    {
        printf("%d) gender %s\n", ii, enumGENDER_TO_STRING[ii]);
    }
}

//------------------------------------------------------------------------------
// you can assign an arbitrary value and/or information to each enum,
#define FOR_EACH_PERSON(tbd) \
        tbd(2, PERSON_FRED,     "Fred",     "Weasley", GENDER_MALE,   12) \
        tbd(4, PERSON_GEORGE,   "George",   "Weasley", GENDER_MALE,   12) \
        tbd(6, PERSON_HARRY,    "Harry",    "Potter",  GENDER_MALE,   10) \
        tbd(8, PERSON_HERMIONE, "Hermione", "Granger", GENDER_FEMALE, 10) \

#define ONE_PERSON_ENUM(value, ename, first, last, gender, age) ename = value,
enum
{
    FOR_EACH_PERSON(ONE_PERSON_ENUM)
};

typedef struct PersonInfoRec
{
    int value;
    const char *ename;
    const char *first;
    const char *last;
    int gender;
    int age;
} PersonInfo;

#define ONE_PERSON_INFO(value, ename, first, last, gender, age) \
                     { ename, #ename, first, last, gender, age },
static const PersonInfo personInfo[] = 
{
    FOR_EACH_PERSON(ONE_PERSON_INFO)
    { 0, NULL, NULL, NULL, 0, 0 }
};
// note: if the enum values are not sequential, you need another way to lookup
// the information besides personInfo[ENUM_NAME]

static void printAllPersons(void)
{
    for (int ii = 0;  ;  ii++)
    {
        const PersonInfo *pPI = &personInfo[ii];
        if (!pPI->ename)
        {
            break;
        }
        printf("%d) enum %-15s  %8s %-8s %13s %2d\n",
            pPI->value, pPI->ename, pPI->first, pPI->last,
            enumGenderToString(pPI->gender), pPI->age);
    }
}

เทคนิคนี้เรียกว่า X-Macro ในกรณีที่มีคนต้องการอ่านมัน นั่นมาจากข้อเท็จจริงที่ว่ามาโคร FOR_EACH_GENDER () นั้นมักจะเรียกว่า X () เสมอ สิ่งหนึ่งที่คุณอาจต้องทำคือ #undef FOR_EACH_GENDER ก่อนที่คุณจะนิยามใหม่ด้วยความหมายใหม่
uliwitness

1

ทุกคำตอบที่นี่บอกเป็นอย่างเดียวกันโดยทั่วไปสร้าง enum ปกติแล้วใช้ตัวรับแบบกำหนดเองเพื่อสลับระหว่างสตริง

ฉันใช้โซลูชันที่เรียบง่ายกว่าที่เร็วกว่าสั้นกว่าและสะอาดกว่าโดยใช้มาโคร!


#define kNames_allNames ((NSArray <NSString *> *)@[@"Alice", @"Bob", @"Eve"])
#define kNames_alice ((NSString *)kNames_allNames[0])
#define kNames_bob ((NSString *)kNames_allNames[1])
#define kNames_eve ((NSString *)kNames_allNames[2])

จากนั้นคุณสามารถเริ่มพิมพ์kNam...และเติมข้อความอัตโนมัติจะแสดงรายการที่คุณต้องการ!

นอกจากนี้หากคุณต้องการจัดการตรรกะสำหรับชื่อทั้งหมดในครั้งเดียวคุณสามารถระบุอาร์เรย์ตัวอักษรได้อย่างรวดเร็วตามลำดับดังนี้:

for (NSString *kName in kNames_allNames) {}

สุดท้าย NSString แคสต์ในมาโครช่วยให้แน่ใจว่าพฤติกรรมคล้ายกับ typedef!


สนุก!


0

มีคำตอบมากมายที่ดีพอสมควร

หากคุณอยู่หลังทั่วไปโซลูชัน Objective C ที่ใช้มาโครบางตัว ...

คุณสมบัติที่สำคัญคือมันใช้ enum เป็นดัชนีในอาร์เรย์คงที่ของค่าคงที่ NSString อาเรย์นั้นถูกรวมเข้ากับฟังก์ชั่นเพื่อให้มันเหมือนชุดฟังก์ชัน NSStringFromXXX ที่แพร่หลายใน Apple APIs

คุณจะต้อง#import "NSStringFromEnum.h"พบที่นี่ http://pastebin.com/u83RR3Vk

[แก้ไข] ยังต้อง#import "SW+Variadic.h"พบที่นี่http://pastebin.com/UEqTzYLf

ตัวอย่างที่ 1: กำหนด enum typedef ใหม่ให้สมบูรณ์พร้อมกับตัวแปลงสตริง

ใน myfile.h


 #import "NSStringFromEnum.h"

 #define define_Dispatch_chain_cmd(enum)\
 enum(chain_done,=0)\
 enum(chain_entry)\
 enum(chain_bg)\
 enum(chain_mt)\
 enum(chain_alt)\
 enum(chain_for_c)\
 enum(chain_while)\
 enum(chain_continue_for)\
 enum(chain_continue_while)\
 enum(chain_break_for)\
 enum(chain_break_while)\
 enum(chain_previous)\
 enum(chain_if)\
 enum(chain_else)\


interface_NSString_Enum_DefinitionAndConverters(Dispatch_chain_cmd)

ใน myfile.m:


 #import "myfile.h"

 implementation_NSString_Enum_Converters(Dispatch_chain_cmd)

ใช้ :

NSString *NSStringFromEnumDispatch_chain_cmd(enum Dispatch_chain_cmd value);

NSStringFromEnumDispatch_chain_cmd(chain_for_c) ผลตอบแทน @"chain_for_c"

  enum Dispatch_chain_cmd enumDispatch_chain_cmdFromNSString(NSString *value);

enumDispatch_chain_cmdFromNSString(@"chain_previous") ผลตอบแทน chain_previous

ตัวอย่างที่ 2: จัดเตรียมรูทีนการแปลงสำหรับ enum ที่มีอยู่แล้วยังแสดงให้เห็นถึงการใช้สตริงการตั้งค่าและการเปลี่ยนชื่อที่ใช้ในฟังก์ชัน

ใน myfile.h


 #import "NSStringFromEnum.h"


 #define CAEdgeAntialiasingMask_SETTINGS_PARAMS CAEdgeAntialiasingMask,mask,EdgeMask,edgeMask

 interface_NSString_Enum_Converters(CAEdgeAntialiasingMask_SETTINGS_PARAMS)

ใน myfile.m:


 // we can put this in the .m file as we are not defining a typedef, just the strings.
 #define define_CAEdgeAntialiasingMask(enum)\
 enum(kCALayerLeftEdge)\
 enum(kCALayerRightEdge)\
 enum(kCALayerBottomEdge)\
 enum(kCALayerTopEdge)



 implementation_NSString_Enum_Converters(CAEdgeAntialiasingMask_SETTINGS_PARAMS)

0

ที่นี่ใช้งานได้ -> https://github.com/ndpiparava/ObjcEnumString

//1st Approach
#define enumString(arg) (@""#arg)

//2nd Approach

+(NSString *)secondApproach_convertEnumToString:(StudentProgressReport)status {

    char *str = calloc(sizeof(kgood)+1, sizeof(char));
    int  goodsASInteger = NSSwapInt((unsigned int)kgood);
    memcpy(str, (const void*)&goodsASInteger, sizeof(goodsASInteger));
    NSLog(@"%s", str);
    NSString *enumString = [NSString stringWithUTF8String:str];
    free(str);

    return enumString;
}

//Third Approcah to enum to string
NSString *const kNitin = @"Nitin";
NSString *const kSara = @"Sara";


typedef NS_ENUM(NSUInteger, Name) {
    NameNitin,
    NameSara,
};

+ (NSString *)thirdApproach_convertEnumToString :(Name)weekday {

    __strong NSString **pointer = (NSString **)&kNitin;
    pointer +=weekday;
    return *pointer;
}

เนื่องจากไม่อนุญาตให้ใช้คำตอบที่ซ้ำกันนี่คือโซลูชันที่สมบูรณ์ github.com/ndpiparava/ObjcEnumString
Nitin

-2

ขึ้นอยู่กับความต้องการของคุณคุณสามารถใช้คำสั่งคอมไพเลอร์เพื่อจำลองพฤติกรรมที่คุณกำลังมองหา

 #define JSON @"JSON"
 #define XML @"XML"
 #define Atom @"Atom"
 #define RSS @"RSS"

เพียงจำข้อบกพร่องของคอมไพเลอร์ตามปกติ (ไม่พิมพ์ปลอดภัยคัดลอกโดยตรงทำให้ไฟล์ต้นฉบับมีขนาดใหญ่ขึ้น)


8
ฉันไม่คิดว่ามันจะใช้งานได้ ทุกที่#defineที่สามารถมองเห็นคุณจะไม่สามารถที่จะใช้ค่า enum ที่เกิดขึ้นจริง (คือJSONจะได้รับการแทนที่ด้วย@"JSON"โดย preprocessor และจะส่งผลในการรวบรวมข้อผิดพลาดเมื่อกำหนดไปFormatType.
แบร์รี่ Wark
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.