NSInteger→longのキャストについて考える

#はじめに
私は、hogeというNSIntegerの変数の値をログ出力する時に、

```objc
NSInteger hoge = 100;
NSLog(@"output hoge = %ld", (long)hoge);
```
といったコードを書いていました。
NSStringでNSIntegerの変数の値を扱う場合は、

```objc
NSInteger hoge = 100;
NSString *hogeString = [NSString stringWithFormat:@"%ld", (long)hoge];
```
みたいな感じですね。

このlongにキャストしているコードについて、
NSNumberを使う or 書式指定子を**%zd**にしましょうと先日教わりました。
(本記事では、NSNumberについては特に触れません。)


そもそもなぜNSIntegerはlongにキャストしなければいけなかったのかなーということを含めて整理します。

#NSIntegerの値をログ出力してみる
###1.NSIntegerの値を指定子%dでログ出力

```objc
NSInteger hoge = 100;
NSLog(@"output hoge = %d", hoge);
```

caseA. iPhone5以前の端末(and iPhone5c)
→怒られない。
<img width="326" alt="スクリーンショット 2016-08-20 12.00.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/103695/ba56f202-4e6f-fb40-f558-612c25989d51.png">

caseB. iPhone5s以降の端末
→怒られる。
<img width="817" alt="スクリーンショット 2016-08-20 11.27.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/103695/3c3bd0a9-409e-7aad-223b-204c9590aba4.png">

指定子%dを使用した場合は、
**32bit端末では怒られないが、64bit端末では怒られる**
ということですね。

###2.NSIntegerの値を指定子%ldでログ出力

```objc
NSInteger hoge = 100;
NSLog(@"output hoge = %ld", hoge);
```
caseA. iPhone5以前の端末(and iPhone5c)
→怒られる。
<img width="1054" alt="スクリーンショット 2016-08-21 2.43.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/103695/3c13b8c1-03f4-cfa1-0ae5-1050bc9f61ee.png">

caseB. iPhone5s以降の端末
→怒られない。
<img width="347" alt="スクリーンショット 2016-08-21 2.45.04.png" src="https://qiita-image-store.s3.amazonaws.com/0/103695/bbc43f86-18c5-f5ac-d393-913629648402.png">

指定子%ldを使用した場合は、
**32bit端末では怒られるが、64bit端末では怒られない**
ということですね。



1.の%dを使用した場合と2.の%ldを使用した場合ともに
「引数にNSIntegerを使用すべきでない。明示的にlongにキャストすべきだよ。」
というような警告が出ています。
警告の通りlongにキャストしてあげれば、32bit端末でも64bit端末でも怒られません。
<img width="348" alt="スクリーンショット 2016-08-20 12.13.15.png" src="https://qiita-image-store.s3.amazonaws.com/0/103695/46332953-d9ca-6ac4-c949-79ef0683f45f.png">

#警告の意味を探る
警告の意味を探るためにNSIntegerがどんなヤツなのか覗いてみました。
<img width="729" alt="スクリーンショット 2016-08-20 11.30.09.png" src="https://qiita-image-store.s3.amazonaws.com/0/103695/84a2129b-f1bb-e6ea-8419-662f4221c630.png">

32bitの場合は、

```objc
typedef int NSInteger;
typedef unsigned int NSUInteger;
```

64bitの場合は、

```objc
typedef long NSInteger;
typedef unsigned long NSUInteger;
```

という定義でした。

先ほど確認した警告は、
32bit端末, 64bit端末ではNSIntegerの定義が違うため発生していた
ということですね。



長たらしくて現実的に使うことは無いですが、
以下のように書いてみたら32bit端末,64bit端末ともに
警告無く、ログ出力できました。

```objc
NSInteger hoge = 100;

#ifdef __LP64__
    NSLog(@"[64bit] output hoge = %ld", hoge);
#else
    NSLog(@"[32bit] output hoge = %d", hoge);
#endif
```

#書式%zdで32bit, 64bit両方に対応
警告の原因はわかりました。
続いて、明示的にキャストしなくても32bit, 64bit両方に対応できるという**%zd**とは何者なのか整理してみます。

%zdは、
指定子%d(符号付き32bit整数)に
長さ修飾子z(size_tまたはこれに類する符号つき整数であることを指定)
をくっつけたものです。
([文字列操作プログラミングガイド](https://developer.apple.com/jp/documentation/Strings.pdf)のp16~ あたりを参考にしました。)

size_tってヤツは、

```objc
(size_t)sizeof(NSInteger)
```
こんな感じで取得できるメモリサイズのことですね。

NSInteger, int, long, size_tのメモリサイズを
sizeof( )で見てみましょう。

caseA. 32bit端末
<img width="568" alt="スクリーンショット 2016-08-21 1.49.21.png" src="https://qiita-image-store.s3.amazonaws.com/0/103695/e94523a9-ba9e-8da0-45b5-18215c731e29.png">




caseB. 64bit端末
<img width="548" alt="スクリーンショット 2016-08-21 1.50.47.png" src="https://qiita-image-store.s3.amazonaws.com/0/103695/35120a15-d372-9a0f-6647-6140c5b6aba6.png">


NSIntegerの定義が32bit端末の場合int型, 64bit端末の場合long型なので、
32bitの場合 
**NSInteger**のメモリサイズ = **int**のメモリサイズ = **4**
64bitの場合 
**NSInteger**のメモリサイズ = **long**のメモリサイズ = **8**
でした。

加えて、32bitの場合, 64bitの場合ともに
**NSInteger**のメモリサイズ = **size_t**のメモリサイズ
ということを確認できました。

だから、以下のように書式%zdを使用すれば32bit, 64bit両方に対応できるといった感じですね。

```objc
NSInteger hoge = 100;
NSLog(@"output hoge = %zd", hoge);
```

#さいごに
確認したことをいろいろ書いたら長くなってしまいましたが一応整理できました。

[文字列操作プログラミングガイド](https://developer.apple.com/jp/documentation/Strings.pdf)
こちらをもっと早くに読んでおけばよかったなーと思いました。