iOS10以降でも32bit端末を意識しなければならない時


はじめに

Intを使うことで、デバイスが64bitか32bitを意識することなくコードを書けるようになってはいたのですが、ハマった箇所があったのでまとめておこうと思います。
※現状で確認できているのは、NSPredicateのみで起こるクラッシュです。

iOS10をサポートしているデバイス

1

その中でも、iPhone5iPhone5ciPad 4thiPad mini 2は32bit端末になります。
最新のOSが32bit端末をサポートしているので、もしbit数まわりでクラッシュが起きる場合には考慮しなくてはならなくなると思います。

端末のbit数を意識しなければならない状況とは?

let id: Int
let fetchRequest: NSFetchRequest
fetchRequest.predicate = NSPredicate(format: "id = %lld", id)

この場合、IntInt64(long long int)であろうことを考慮して、%lldに指定するかと思います。
しかしながら、%lldでは32bit端末で実行時エラーとなってしまいます。
String(format:_:)ではクラッシュせずに値がおかしくなてしまうだけですが、NSPredicate(format:_:)ではクラッシュしてしまいます。

これを簡単に解決する方法は%lld%ldにすることかと思います。

下記のようにsizeofValue(id)sizeof(Int32)を比較して、%ldまたは%lldを出し分けるという方法もあります。

let id: Int
let fetchRequest: NSFetchRequest
//32bit端末対応
let key = sizeofValue(id) == sizeof(Int32) ? "%ld" : "%lld"
fetchRequest.predicate = NSPredicate(format: "id = \(key)", id)

ちなみにObjective-Cでは...

32bit端末で%lldを実行しても、値がおかしくなってしまうだけでクラッシュはしませんでした。

NSInteger id = 10; 
NSPredicate *predicate = [NSPredicate predicateWithFormat:@"id = %lld", id];
NSLog(@"%@", predicate);

最後に

Int32の最大値は2,147,483,647なので、そもそも最大値を越えた場合は32bit端末で正常な動作はしないのですが、32bitと64bitの端末はまだ存在しているということを頭の片隅に置いておくと、不要なクラッシュを防ぐことができるかもしれません。

2016/10/05追記

下記のコードでも対応可能でした。

let id: Int
let fetchRequest: NSFetchRequest
fetchRequest.predicate = NSPredicate(format: "id = %@", NSNumber(value: id))

分岐を書く必要がないので、NSNumberを使うとスッキリするかもしれません。