このように列挙を宣言するとき、enumの定数のデータ型はunsigned intではなくNSUIntegerであることがどのように保証されますか:
enum {
NSNullCellType = 0,
NSTextCellType = 1,
NSImageCellType = 2
};
typedef NSUInteger NSCellType;
NSUIntegerのtypedefは、何らかの方法でenum宣言に結び付けられていないようです。
列挙値に関するガイダンスについては、AppleのCocoaの64ビット移行ガイドを読んでいたところ、質問がありました。以下は、列挙定数セクションからの(長い)引用です。
列挙(enum)定数の問題は、それらのデータ型が頻繁に不定であることです。言い換えると、enum定数は予測可能な符号なしintではありません。従来の構成の列挙では、コンパイラーは実際に検出した内容に基づいて基になる型を設定します。基になる型は、(署名された)intまたはlongです。次の例をご覧ください。
type enum {
MyFlagError = -1,
MyFlagLow = 0,
MyFlagMiddle = 1,
MyFlagHigh = 2
} MyFlagType;
コンパイラはこの宣言を見て、メンバー定数の1つに割り当てられた負の値を見つけて、列挙型intの基になる型を宣言します。メンバーの値の範囲がintまたはunsigned intに収まらない場合、基本型は暗黙的に64ビット(long)になります。したがって、列挙として定義された数量の基本タイプは、列挙内の値に合わせてサイズを静かに変更できます。これは、32ビットまたは64ビットのどちらをコンパイルしていても発生する可能性があります。言うまでもなく、この状況はバイナリ互換性の障害となります。
この問題の解決策として、AppleはCocoa APIの列挙型についてより明確にすることを決定しました。用語で宣言する代わりに列挙のヘッダーファイルは、サイズを指定できる列挙の型を個別に宣言するようになりました。列挙のメンバーとその値は、以前のように宣言され、割り当てられます。
typedef enum {
NSNullCellType = 0,
NSTextCellType = 1,
NSImageCellType = 2
} NSCellType;
今これがあります:
enum {
NSNullCellType = 0,
NSTextCellType = 1,
NSImageCellType = 2
};
typedef NSUInteger NSCellType;
列挙型は、NSIntegerまたはNSUIntegerで定義され、64ビットアーキテクチャでベース列挙型を64ビットに対応させます。
私の質問はこれです:typedefがenum宣言に明示的に関連付けられていないように見えるので、データ型がunsigned intまたはNSUIntegerであるかどうかをどのように知ることができますか?
NS_ENUM
Xcode 4.5の開始:
typedef NS_ENUM(NSUInteger, NSCellType) {
NSNullCellType = 0,
NSTextCellType = 1,
NSImageCellType = 2
};
NS_OPTIONS
バイナリフラグを使用する場合:
typedef NS_OPTIONS(NSUInteger, MyCellFlag) {
MyTextCellFlag = 1 << 0,
MyImageCellFlag = 1 << 1,
};
シミュレーターでテストを実行するため、テストの目的はさまざまな整数型のサイズをチェックすることです。そのため、sizeof
の結果がコンソールに出力されました。したがって、このenum
値をテストします。
typedef enum {
TLEnumCero = 0,
TLEnumOne = 1,
TLEnumTwo = 2
} TLEnum;
typedef enum {
TLEnumNegativeMinusOne = -1,
TLEnumNegativeCero = 0,
TLEnumNegativeOne = 1,
TLEnumNegativeTwo = 2
} TLEnumNegative;
typedef NS_ENUM(NSUInteger, TLUIntegerEnum) {
TLUIntegerEnumZero = 0,
TLUIntegerEnumOne = 1,
TLUIntegerEnumTwo = 2
};
typedef NS_ENUM(NSInteger, TLIntegerEnum) {
TLIntegerEnumMinusOne = -1,
TLIntegerEnumZero = 0,
TLIntegerEnumOne = 1,
TLIntegerEnumTwo = 2
};
テストコード:
NSLog(@"sizeof enum: %ld", sizeof(TLEnum));
NSLog(@"sizeof enum negative: %ld", sizeof(TLEnumNegative));
NSLog(@"sizeof enum NSUInteger: %ld", sizeof(TLUIntegerEnum));
NSLog(@"sizeof enum NSInteger: %ld", sizeof(TLIntegerEnum));
iPhone Retina(4-inch)Simulatorの結果:
sizeof enum: 4
sizeof enum negative: 4
sizeof enum NSUInteger: 4
sizeof enum NSInteger: 4
iPhone Retina(4インチ64ビット)シミュレーターの結果:
sizeof enum: 4
sizeof enum negative: 4
sizeof enum NSUInteger: 8
sizeof enum NSInteger: 8
結論
汎用のenum
は、int
またはunsigned int
32または64ビットの4バイトのタイプ。既に知っているように、NSUInteger
とNSInteger
は、32ビットの場合は4バイト、iOSの64ビットコンパイラの場合は8バイトです。
これらは2つの別々の宣言です。 typedefは、そのタイプを使用すると、常にNSUIntegerを取得することを保証します。
列挙型の問題は、値を保持するのに十分な大きさではないということではありません。実際、enumで得られる唯一の保証は、sizeof(enum Foo)がそのenumで現在定義されている値を保持するのに十分な大きさであることです。ただし、別の定数を追加すると、サイズが変わる場合があります。だからこそApple APIのバイナリ安定性を維持するために、個別のtypedefを実行してください。
列挙型の定数のデータ型はNSUInteger
であるとは限りませんが、NSUInteger
で使用するたびにNSCellType
にキャストされることが保証されます。
つまり、宣言では、列挙型の値は現在unsigned int
、NSCellType
を介してアクセスするときにそれらのために予約されたストレージはNSUInteger
でなければなりません。