.hファイルでtypedefが宣言されているとします:
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値をメソッドに渡すと、エラーが発生します。私は何が欠けていますか?
これは実際にはCの質問であり、Objective-C(C言語のスーパーセット)に固有のものではありません。 Cの列挙は整数として表されます。したがって、列挙値を指定して文字列を返す関数を作成する必要があります。これを行うには多くの方法があります。列挙値を文字列にマッピングする配列またはマップ構造(たとえば、NSDictionary
)へのインデックスとして列挙値を使用できるような文字列の配列ですが、これらのアプローチは変換を明示的にする関数としてクリアします(そして、列挙型の値が0から連続していない場合、古典的なC
の方法は危険ですが、配列アプローチを使用します)。このような何かが動作します:
- (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;
}
列挙値の正しい構文に関する関連する質問は、FormatType.JSON
sytaxではなく、値(たとえば、JSON
)のみを使用することです。 FormatType
はタイプであり、列挙値(たとえば、JSON
、XML
など)は、そのタイプに割り当てることができる値です。
簡単にはできません。 CおよびObjective-Cでは、enumは実際には単なる美化された整数定数です。名前のテーブルを自分で生成する必要があります(またはプリプロセッサを乱用する場合もあります)。例えば:
// 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];
このアプローチの危険性は、列挙型を変更した場合、名前の配列を変更することを忘れないでください。この問題は、プリプロセッサを乱用することで解決できますが、厄介でtrickいです。
また、これは有効な列挙定数があることを前提としていることに注意してください。信頼できないソースからの整数値がある場合は、さらに、定数が有効であることを確認する必要があります。列挙に「過去の最大」値を含めるか、配列の長さsizeof(FormatType_toString) / sizeof(FormatType_toString[0])
より小さいかどうかを確認します。
私の解決策:
編集:最後に、モダンなObj-Cを使用してより良いソリューションを追加しました
1。
名前を配列のキーとして入力します。
インデックスが適切な列挙型であることを確認してくださいおよび正しい順序で(それ以外の場合は例外)。
注:namesは、* _ 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を使用すると、辞書を使用して列挙型のキーに説明を関連付けることができます。
順序は関係ありません。
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]);
@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]);
最悪の場合-列挙型を変更したが、名前の配列を変更するのを忘れた場合-このキーに対してnilを返します。
クラスヘッダーでtypedef列挙型を定義します。
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";
私はコンパイラの#文字列トークンを使用します(マクロとともにマクロをすべてコンパクトにします):
#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
}
私は#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];
}
ソース (ソースはもう利用できません)
このページにあるすべてのソリューションを組み合わせて、私のものを作成しました。これは、オブジェクト指向の列挙型拡張機能のようなものです。
実際、定数(つまり整数)以上のものが必要な場合は、おそらくモデルオブジェクトが必要です(すべてMVCについて話しているのですか?)
これを使用する前に質問をしてください。実際、Webサービス、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
別の解決策:
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);
文字列依存関係を削除することで@ 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エントリ名を変更すると、対応する文字列が変更されます。この文字列をユーザーに表示しない場合に役立ちます。
私は、バリーウォークの回答のバリエーションを使用します。
例えば:
- (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!
}
NSDictionary
ルックアップに変換したい大きな列挙型がありました。 OSX端末からsed
を次のように使用することになりました。
$ sed -E 's/^[[:space:]]{1,}([[:alnum:]]{1,}).*$/ @(\1) : @"\1",/g' ObservationType.h
次のように読み取ることができます: '行の最初のWordをキャプチャし、@(Word):@ "Word"、'を出力します。
この正規表現は、以下を含む「ObservationType.h」という名前のヘッダーファイルの列挙型を変換します。
typedef enum : int {
ObservationTypePulse = 1,
ObservationTypeRespRate = 2,
ObservationTypeTemperature = 3,
.
.
}
次のようなものに:
@(ObservationTypePulse) : @"ObservationTypePulse",
@(ObservationTypeRespRate) : @"ObservationTypeRespRate",
@(ObservationTypeTemperature) : @"ObservationTypeTemperature",
.
.
その後、最新のObjective-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
ボイラープレートは、静的変数がスレッドセーフな方法で初期化されるようにするためのものです。
注:OSXでsed正規表現式がおかしいことがわかりました-+
を使用して「1つ以上」を一致させようとしても機能せず、代わりに{1,}
を使用する必要がありました
ここでいくつかのアプローチを組み合わせました。プリプロセッサとインデックス付きリストのアイデアが好きです。
余分な動的割り当てはありません。また、インライン展開のために、コンパイラがルックアップを最適化できる場合があります。
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];
}
@pixelが最も素晴らしい答えをここに追加しました: https://stackoverflow.com/a/24255387/1364257 どうか、彼に賛成してください!
彼は1960年代のきちんとしたXマクロを使用しています。 (私は彼のコードを現代の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
次のような列挙型定義が与えられた場合:
typedef NS_ENUM(NSInteger, AssetIdentifier) {
Isabella,
William,
Olivia
};
以下に示すように、列挙値を対応する文字列に変換するマクロを定義できます。
#define AssetIdentifier(asset) \
^(AssetIdentifier identifier) { \
switch (identifier) { \
case asset: \
default: \
return @#asset; \
} \
}(asset)
ブロックで使用されるswitch
ステートメントは型チェック用であり、Xcodeでオートコンプリートサポートを取得するためにも使用されます。
まず、FormatType.JSONに関して、JSONはFormatTypeのメンバーではなく、型の可能な値です。 FormatTypeは複合型ではなく、スカラーです。
第二に、これを行う唯一の方法はマッピングテーブルを作成することです。 Objective-Cでこれを行うより一般的な方法は、「シンボル」を参照する一連の定数を作成して、NSString *FormatTypeJSON = @"JSON"
などを取得することです。
以下は、新しい列挙型を追加するのに1行の編集のみが必要なソリューションを提供します。これは、列挙型{}リストに単一行を追加するのと同様の作業です。
//------------------------------------------------------------------------------
// 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);
}
}
ここのすべての答えは基本的に同じことを言っており、通常の列挙型を作成してからカスタムゲッターを使用して文字列を切り替えます。
マクロを使用して、より速く、短く、きれいなはるかに単純なソリューションを採用しています!
#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と同様の動作が保証されます!
楽しい!
多くの回答はすべてかなり良いです。
いくつかのマクロを使用する汎用のObjective Cソリューションを使用している場合...
主な機能は、enumをNSString定数の静的配列へのインデックスとして使用することです。配列自体は関数にラップされて、Apple APIで一般的なNSStringFromXXX関数のスイートのようになります。
ここで#import "NSStringFromEnum.h"
を見つける必要があります http://Pastebin.com/u83RR3Vk
[編集]には#import "SW+Variadic.h"
も必要です http://Pastebin.com/UEqTzYLf
例1:文字列コンバーターを使用して、新しい列挙型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:既存の列挙型に変換ルーチンを提供することは、設定文字列の使用と、関数で使用される型名の変更を示しています。
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)
動作しています-> 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;
}