NSIntegerとかCGFloatとか
NSInteger/ NSUInteger
NSInteger
やNSUInteger
は32bit/64bit環境では異なる型として扱われる。NSIntegerの場合、32bit環境ではint
、64bit環境ではlong
として扱われる。
なおarm64
の処理系ではint
型そのものは32bitの値として扱われる。それまでのarm7
まではint
もlong
も同じ32bitの値として扱われていたので差がなかったのが(意外に知らない人が多い)、arm64
にてlong
が倍精度に変更された。
そのため、
NSInteger aValue = 100;
NSLog(@"%d", aValue);
例えばこんなコードも、64bitアーキテクチャを対象に含めると警告が出てしまう(NSInteger
がlong
のため、%ld
を使う必要がある)。もっとも、aValue
の値が32bitの範囲を超えない限り、実際に問題になることはないのだけど。
警告を潰したい場合、現状ではlong
にキャストして%ld
で出力するのが一番安全。将来的なところを考えると微妙としか思えないけど。
NSInteger aValue = 100;
NSLog(@"%ld", (long)aValue);
実際に問題になるケースもある。例えば次のようなコード。
NSArray *aArray = @[@"The", @"cow", @"jumped", @"over", @"the", @"moon"];
unsigned int foundIndex = [aArray indexOfObject:@"hoge"];
if (foundIndex == NSNotFound){
NSLog(@"hoge is not found");
return ERROR_NOT_FOUND;
}
NSArray
のindexOfObject:
の返却型はNSUInteger
であり、NSNotFound
はNSIntegerMax
として定義されているので、 32bitと64bit環境で値が異なる 。
そのため、indexOfObject:
の返却値をunsigned int
に代入するとオーバーフローが発生し、NSNotFound
との比較文で必ず失敗する。
もっとも警告が大量に出るので、それを見逃さない限りは大丈夫なはずなんだけど。
返却値がtypedef
されている型になっているものについては、必ずQuick HelpやClass Referenceを参照して、その型で受け取るようにする…というのを守れば基本的には問題がないはず。
あとCore DataにNSInteger
の値は格納しない方が無難、…なのかなぁ。32bit端末と64bit端末間でデータ連携された際の対策が思い浮かばない。
CGFloat
CGFloat
も同様に32bit環境ではfloat
型として、64bit環境ではdouble
型として扱われる。
arm7
以前ではint
、long
ともに32bitだったのに対して、float
は32bit、double
は64bitの値であり、arm64
でも変更はない。
CGFloat
のプレフィクスはCoreGraphicsからきており、タッチ座標系の値で頻繁に用いられる。そのため、一時的にオブジェクトにラップして、NSDictionary
等に格納したいというケースが生じると思う。
CGFloat
からNSNumber
への変換は、簡易記法を用いて以下のように書ける。
CGFloat aValue = 10.5f;
NSNumber aNumber = @(aValue);
問題は、 逆の変換が存在しない ということ。
NSInteger
の場合、numberWithInteger:
でラップして、integerValue
で元に戻すことができるが、CGFloat
にはそれに相当するnumberWithCGFloat:
やCGFloatValue
が用意されていない。
もし扱いたい値がCGRect
やCGPoint
であればそのまま扱うこと。構造体のままであればNSValue
でオブジェクト化/解除ができる。「正方形を扱うならCGSize
じゃもったいないからCGFloat
で保存しよう」とか考えるとかえって面倒なことになる。
CGFLOAT_IS_DOUBLE
マクロを利用することで、対応することはできる。以下のような処理をNSNumber
のカテゴリで定義するのが手っ取り早い。
#if CGFLOAT_IS_DOUBLE
CGFloat reverseValue = [aNumber doubleValue];
#else
CGFloat reverseValue = [aNumber floatValue];
#endif
もっともCGFloat
に求められる精度を考えると、float
として扱っても実害はないのかもしれない。