NSLogでNSInteger変数を出力する際の注意

2014. 05. 19
詳細! Objective-C iPhoneアプリ開発 入門ノート Xcode5+iOS7対応」を読み始めて、NSInteger変数をNSLogで出力する際に(「Part 2 Objective-Cの基礎知識」の「Chapter 2-1 Objective-Cのプログラム」の「変数の宣言とデータ型(p.35)」など)、32bit環境では問題ないのですが64bit環境では警告が出るので調べてみました。



●警告内容

警告文は「Value of type 'NSInteger' should not be used as format arguments; add an explicit cast to 'long' instead(NSInteger型の値はフォーマット引数として使用しないでください。代わりにlongへの明示的なキャストを追加してください)」とあり、(long)でキャストすると共に書式指定子%dを%ldに変更するようにと指示されます。

8036.jpg



●原因

これはiOSの64ビット化に伴い、一部のデータ型のバイト長が32ビットと64ビットのランタイム環境間で異なるためです。

Cocoa Touch 64ビット移行ガイド」の「データ型に関する変更点」の「2つの規約:ILP32とLP64(p.8)」に、これまでの32bitランタイムでの規約(ILP32)と新たに追加された64bitランタイムでの規約(LP64)におけるバイト長と揃え境界(アライメント)が示されています。

表1-1 OS XおよびiOSにおける、各種整数型のバイト長と揃え境界

整数型バイト長
(ILP32)
揃え境界
(ILP32)
バイト長
(LP64)
揃え境界
(LP64)
char1バイト1バイト1バイト1バイト
BOOL、bool1バイト1バイト1バイト1バイト
short2バイト2バイト2バイト2バイト
int4バイト4バイト4バイト4バイト
long4バイト4バイト8バイト8バイト
long long8バイト4バイト8バイト8バイト
ポインタ4バイト4バイト8バイト8バイト
size_t4バイト4バイト8バイト8バイト
time_t4バイト4バイト8バイト8バイト
NSInteger4バイト4バイト8バイト8バイト
CFIndex4バイト4バイト8バイト8バイト
fpos_t8バイト4バイト8バイト8バイト
off_t8バイト4バイト8バイト8バイト

表1-2 OS XおよびiOSにおける、浮動小数点数型のバイト長と揃え境界

浮動小数点数型バイト長
(ILP32)
揃え境界
(ILP32)
バイト長
(LP64)
揃え境界
(LP64)
float4バイト4バイト4バイト4バイト
double8バイト8バイト8バイト8バイト
CGFloat4バイト4バイト8バイト8バイト

NSIntegerはNSObjCRuntime.hで次のように定義されています。

#if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64
typedef long NSInteger;
typedef unsigned long NSUInteger;
#else
typedef int NSInteger;
typedef unsigned int NSUInteger;
#endif

32bitランタイムではint(4バイト)でしたが、64bitランタイムではlong(8バイト)に定義が変更されています。

このためNSInteger変数をlongにキャストし、「文字列操作プログラミングガイド」の「書式指定子(p.16)」にあるようにint指定子の%dからlong指定子の%ldにする必要があるのです。

表1 NSStringやCFStringの整形メソッド/関数で使える書式指定子

指定子説明
%@Objective-Cのオブジェクトの文字列表現。descriptionWithLocale:メソッドがあればその戻り値、なければdescriptionメソッドの戻り値になります。CFTypeRefオブジェクトに対しても適用可能で、この場合、CFCopyDescription関数の戻り値を使います。
%%'%'という文字自身。
%d、%D符号付き32ビット整数(int)。
%u、%U符号なし32ビット整数(unsigned int)。
%x符号なし32ビット整数(unsigned int)の16進表示。数字(0-9)と英小文字(a-f)で表記。
%X符号なし32ビット整数(unsigned int)の16進表示。数字(0-9)と英小文字(A-F)で表記。
%o、%O符号なし32ビット整数(unsigned int)の8進表示。
%f64ビット浮動小数点数(double)。
%e64ビット浮動小数点数(double)の指数表記。指数部の先頭は小文字の「e」。
%E64ビット浮動小数点数(double)の指数表記。指数部の先頭は大文字の「E」。
%g64ビット浮動小数点数(double)。指数部が-4未満または精度以上であれば%eと同じ、そうでなければ%fと同じ。
%G64ビット浮動小数点数(double)。指数部が-4未満または精度以上であれば%Eと同じ、そうでなければ%fと同じ。
%c8ビット符号なし文字(unsigned char)。NSLog()でASCII文字として出力。非ASCII文字は8進形式(\\ddd)またはUnicodeの16進形式(\\udddd、但しdは数字)。
%C16ビットUnicode文字(unichar)。NSLog()でASCII文字として出力。非ASCII文字は8進形式(\\ddd)またはUnicodeの16進形式(\\udddd、但しdは数字)。
%s8ビット符号なし文字の配列(末尾はNull)。%s指定子があると、システムデフォルトのエンコーディングとして文字を解釈するので、特に書字方向が右から左(RTL、Right-To-Left)の言語の場合、結果が変わる可能性があります。たとえば強い方向付けがない文字の場合、書字方向マーカが挿入されるのです。したがって、%sの使用を避け、エンコーディングを明示的に指定する方がよいでしょう。
%S16ビットUnicode文字の配列(末尾はNull)。
%pvoidポインタ(void *)。先頭に「0x」を置いた16進表示。数字(0-9)および英小文字(a-f)で表記。
%a64ビット浮動小数点数(double)の指数表記。先頭に「0x」、小数点の前に16進数字を1つ、指数部の先頭は小文字の「p」。
%A64ビット浮動小数点数(double)の指数表記。先頭に「0x」、小数点の前に16進数字を1つ、指数部の先頭は大文字の「P」。
%F64ビット浮動小数点数(double)の10進表記。

表2 NSStringやCFStringの整形メソッド/関数で使える長さ修飾子

長さ修飾子説明
h変換指定子d、o、u、x、Xの前に置いて、引数がshortまたはunsigned shortであることを指定。
hh変換指定子d、o、u、x、Xの前に置いて、引数がsigned charまたはunsigned charであることを指定。
l変換指定子d、o、u、x、Xの前に置いて、引数がlongまたはunsigned longであることを指定。
ll、q変換指定子d、o、u、x、Xの前に置いて、引数がlong longまたはunsigned long longであることを指定。
L変換指定子a、A、e、E、f、F、g、Gの前に置いて、引数がlong doubleであることを指定。
z変換指定子d、o、u、x、Xの前に置いて、引数がsize_tまたはこれに類する符号つき整数であることを指定。
t変換指定子d、o、u、x、Xの前に置いて、引数がptrdiff_tまたはこれに類する符号なし整数であることを指定。
j変換指定子d、o、u、x、Xの前に置いて、引数がintmax_tまたはuintmax_tであることを指定。



●対処法

例として以下のコードを使用します。

NSInteger a=10, b=20, ans;
ans = a + b;
NSLog(@"ans = %d", ans);


1)NSInteger値をlongにキャストする

NSInteger a=10, b=20, ans;
ans = a + b;
NSLog(@"ans = %ld", (long)ans);

コンパイラが指示するもので、「文字列操作プログラミングガイド」の「プラットフォームによる違い(p.18)」にあるようにようにAppleが推奨する対処法です。

表3 データ型に応じた書式指定子

書式指定子考慮点
NSInteger%ldまたは%lx値をlongにキャスト。
NSUInteger%luまたは%lx値をunsigned longにキャスト。
CGFloat%fまたは%g整形の場合、%fはfloat、doubleの両方に有効。ただし、書式指定に従って文字列を解析するときは、下記(「文字列操作プログラミングガイド」のp.19)の注意点を参照。
CFIndex%ldまたは%lxNSIntegerと同様。
ポインタ%pまたは%zx%pならば先頭に「0x」と印字。これが不要であれば%zxを指定し、型のキャストはしない。

この対処法は確かに32/64bit両ランタイム環境で通じるのですが、同様にfloatからdoubleへとバイト長の変わったCGFloatなども含め、適切な書式指定子とキャストを施さなければならないのが少々面倒です。


2)長さ修飾子zを利用する

NSInteger a=10, b=20, ans;
ans = a + b;
NSLog(@"ans = %zd", ans);

size_tがNSIntegerと同じく32bit環境では4バイト、64bit環境では8バイトであることから、長さ修飾子zを利用するというものです。
(「Qiita/Objective-C/32bitと64bit、共通で使えるNSLogとかのフォーマット指定子」や「暇人のブログ/NSIntegerを32bit/64bit両環境で表示させるには」を参照)

この対処法は動作しては問題ないものの本来意味する長さ修飾子ではないので、急場凌ぎとして一時的な措置なら構わないと思いますが、複数人がコードを編集するような場合は混乱を招くので避けた方が良いでしょう。


3)NSNumberリテラルを利用する

NSInteger a=10, b=20, ans;
ans = a + b;
NSLog(@"ans = %@", @(ans));

Xcode 4.4から導入されたNSNumberリテラルを使用して、NSInteger値をNSNumberオブジェクトとする方法です。
(「[yashigani days]/最近はやってるNSLogの書き方」や「Natsu's note/[Xcode][Modern Objectice-C] NSNumberリテラルとBoxed Expression Literals」を参照)

この対処法であればランタイム環境による変数のバイト長の変化を気にする必要が無く、画一的かつ簡素に表現できる良い方法だと思います。



参考文献

Cocoa Touch 64ビット移行ガイド

文字列操作プログラミングガイド

Qiita/Objective-C/32bitと64bit、共通で使えるNSLogとかのフォーマット指定子

暇人のブログ/NSIntegerを32bit/64bit両環境で表示させるには

[yashigani days]/最近はやってるNSLogの書き方

Natsu's note/[Xcode][Modern Objectice-C] NSNumberリテラルとBoxed Expression Literals

詳細! Objective-C iPhoneアプリ開発 入門ノート Xcode5+iOS7対応詳細! Objective-C iPhoneアプリ開発 入門ノート Xcode5+iOS7対応
(2013/11/02)
大重 美幸

商品詳細を見る






bose_soundlink_revolve
0 Comments
Leave a comment
管理者にだけ表示を許可する
Top
0 Trackbacks
Top
Calendar
03 | 2017/04 | 05
Sun Mon Tue Wed Thu Fri Sat
- - - - - - 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 - - - - - -
Recent Articles
iTunes


Swift
Categories
Tips
Profile

水月杏香

Author:水月杏香
永遠の初心者プログラマ。

Wish List
WACOM


ARC
Technical Q&A
情報プロパティリストキー
Start Developing iOS Apps Today
BOSE

bose_soundlink_revolve
Reference
NSApplicationDelegateプロトコル
NSArrayクラス
NSAutoreleasePoolクラス
NSBundleクラス
NSBundle UIKit追加分
NSCalendarクラス
NSCoderクラス
NSCodingプロトコル
NSCopyingプロトコル
NSDataクラス
NSDateクラス
NSDateFormatterクラス
NSDictionaryクラス
NSEntityDescriptionクラス
NSEnumeratorクラス
NSErrorクラス
NSExceptionクラス
NSFetchRequestクラス
NSFileHandleクラス
NSFileManagerクラス
NSIndexPathクラス
NSIndexPath UIKit追加分
NSKeyedArchiverクラス
NSKeyedUnarchiverクラス
NSKeyValueCodingプロトコル
NSLocaleクラス
NSManagedObjectクラス
NSManagedObjectContextクラス
NSManagedObjectModelクラス
NSMutableArrayクラス
NSMutableCopyingプロトコル
NSMutableDictionaryクラス
NSMutableSetクラス
NSNotificationクラス
NSNotificationCenterクラス
NSNullクラス
NSNumberクラス
NSObjectクラス
NSObject UIKit追加分
NSObjectプロトコル
NSPersistentStoreクラス
NSPersistentStoreCoordinatorクラス
NSPredicateクラス
NSPropertyListSerializationクラス
NSRunLoopクラス
NSSetクラス
NSStringクラス
NSString UIKit追加分
NSTimerクラス
NSTimeZoneクラス
NSURLクラス
NSURLProtectionSpaceクラス
NSURLRequestクラス
NSUserDefaultsクラス
NSValueクラス

UIActionSheetクラス
UIActionSheetDelegateプロトコル
UIActivityIndicatorViewクラス
UIAlertViewクラス
UIAlertViewDelegateプロトコル
UIApplicationクラス
UIApplicationDelegateプロトコル
UIBarButtonItemクラス
UIBarItemクラス
UIButtonクラス
UIColorクラス
UIControlクラス
UIDatePickerクラス
UIDeviceクラス
UIEventクラス
UIFontクラス
UIGestureRecognizerクラス
UIImageクラス
UIImageViewクラス
UIKit Function
UILabelクラス
UINavigationControllerクラス
UINavigationItemクラス
UIPickerViewクラス
UIPickerViewDataSourceプロトコル
UIPickerViewDelegateプロトコル
UIPinchGestureRecognizerクラス
UIResponderクラス
UIScreenクラス
UIScrollViewクラス
UISearchBarクラス
UISearchBarDelegateプロトコル
UISegmentedControlクラス
UISliderクラス
UISwipeGestureRecognizerクラス
UISwitchクラス
UITableViewクラス
UITableViewCellクラス
UITableViewControllerクラス
UITableViewDataSourceプロトコル
UITableViewDelegateプロトコル
UITapGestureRecognizerクラス
UITextFieldクラス
UITextFieldDelegateプロトコル
UITextInputTraitsプロトコル
UITextViewクラス
UITextViewDelegateプロトコル
UIToolbarクラス
UITouchクラス
UIViewクラス
UIViewControllerクラス
UIWebViewクラス
UIWebViewDelegateプロトコル
UIWindowクラス

AVAudioPlayerクラス
AVAudioPlayerDelegateプロトコル

CADisplayLinkクラス
CAEAGLLayerクラス
CALayerクラス

CGAffineTransform
CGBitmapContext
CGColor
CGColorSpace
CGContext
CGGeometry
CGImage
CGPath

EAGLContextクラス
EAGLDrawableプロトコル

Foundation Constants
Foundation Data Types
Foundation Functions

MPMediaItemクラス
MPMediaItemArtworkクラス
MPMediaPlaylistクラス
MPMediaPropertyPredicateクラス
MPMediaQueryクラス
MPMusicPlayerControllerクラス

Randomization Services

System Sound Services
Amazon


OpenGL ES
SQLite
Monthly Archives
Recent Comments
Recent TrackBacks
RSS Link
Visitors
QR Code
QR