読者です 読者をやめる 読者になる 読者になる

なるようになるかも

力は多くの場合、その人の思いを超えない。

NSIntegerとかCGFloatとか

NSInteger/ NSUInteger

NSIntegerNSUIntegerは32bit/64bit環境では異なる型として扱われる。NSIntegerの場合、32bit環境ではint、64bit環境ではlongとして扱われる。

なおarm64の処理系ではint型そのものは32bitの値として扱われる。それまでのarm7まではintlongも同じ32bitの値として扱われていたので差がなかったのが(意外に知らない人が多い)、arm64にてlongが倍精度に変更された。

そのため、

NSInteger aValue = 100;
NSLog(@"%d", aValue);

例えばこんなコードも、64bitアーキテクチャを対象に含めると警告が出てしまう(NSIntegerlongのため、%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;
}

NSArrayindexOfObject:の返却型はNSUIntegerであり、NSNotFoundNSIntegerMaxとして定義されているので、 32bitと64bit環境で値が異なる

そのため、indexOfObject:の返却値をunsigned intに代入するとオーバーフローが発生し、NSNotFoundとの比較文で必ず失敗する。

もっとも警告が大量に出るので、それを見逃さない限りは大丈夫なはずなんだけど。

返却値がtypedefされている型になっているものについては、必ずQuick HelpやClass Referenceを参照して、その型で受け取るようにする…というのを守れば基本的には問題がないはず。

あとCore DataにNSIntegerの値は格納しない方が無難、…なのかなぁ。32bit端末と64bit端末間でデータ連携された際の対策が思い浮かばない。

CGFloat

CGFloatも同様に32bit環境ではfloat型として、64bit環境ではdouble型として扱われる。

arm7以前ではintlongともに32bitだったのに対して、floatは32bit、doubleは64bitの値であり、arm64でも変更はない。

CGFloatのプレフィクスはCoreGraphicsからきており、タッチ座標系の値で頻繁に用いられる。そのため、一時的にオブジェクトにラップして、NSDictionary等に格納したいというケースが生じると思う。

CGFloatからNSNumberへの変換は、簡易記法を用いて以下のように書ける。

CGFloat aValue = 10.5f;
NSNumber aNumber = @(aValue);

問題は、 逆の変換が存在しない ということ。

NSIntegerの場合、numberWithInteger:でラップして、integerValueで元に戻すことができるが、CGFloatにはそれに相当するnumberWithCGFloat:CGFloatValueが用意されていない。

もし扱いたい値がCGRectCGPointであればそのまま扱うこと。構造体のままであればNSValueでオブジェクト化/解除ができる。「正方形を扱うならCGSizeじゃもったいないからCGFloatで保存しよう」とか考えるとかえって面倒なことになる。

CGFLOAT_IS_DOUBLEマクロを利用することで、対応することはできる。以下のような処理をNSNumberのカテゴリで定義するのが手っ取り早い。

#if CGFLOAT_IS_DOUBLE
    CGFloat reverseValue = [aNumber doubleValue];
#else
    CGFloat reverseValue = [aNumber floatValue];
#endif

もっともCGFloatに求められる精度を考えると、floatとして扱っても実害はないのかもしれない。