NSDictionaryを使う時に気になったこと:定数定義とクラスキャスト

iOSフレームワークには、NSDictionary といういわゆるハッシュマップのようなキー/バリューストアとして使えるものがある。これを使っていて気になったことをメモ。

予め決まったキーを使う場合、普通それを定数としておきたいと思う。定数を定義するのには define ではなく const を使う、というのは以前の通り。しかし、以下のような書き方では警告がでる。

const NSString *key = @"someone";
・・・
id *value = [dictionary valueForKey:key]
(ここで警告:warning: passing argument 1 of 'valueForKey:' discards qualifiers from pointer target type)

これは、const を書く順番に問題がある。

const NSString * key = @"..."; // pointer to const
  or
NSString const * key = @"...";

上記の書き方は、const な NSStringオブジェクトへのポインタとなる。 [dictionary valueForKey:] で引数にとる型は、(NSString *)key のため、ここで型の不一致が起きてしまう。それを回避するためには以下のように定義する。

NSString * const key = @"..."; // const pointer

これは、NSStringオブジェクトへのポインタの const となり、型は (NSString *) のため、警告は出ない。なお、前述の書き方でも別に動かないわけではない。なら警告ださなくても、と思わなくもない。正直なところ、この二つの違いがどう影響するのかまでよくわかっていません。。。

ちなみに、グローバル変数としてkeyを定義すると、他のソースファイルに影響することがある。そういう場合は、static をつけることで、定義しているファイル内からだけアクセスを許可するようにできる。逆に、他のファイルと共有したい場合は static はつけてはいけない。

(追記:他のファイルから読み込む場合、ヘッダファイル中では extern で宣言だけ行い、定義は実装ファイルで行うようにする。)

ところで、最初のサンプルコードのように id 型で返ってきたオブジェクトを適切な型にキャストしたことが多々ある。Objective-C でクラスキャストの方法が分からなかったので調べた。普通に(型名)で良いようだ。ただし、オブジェクトはポインタなので、ポインタ演算子*をつけること。
例)

  id *value = [dictionary valueForKey:key]
  MyClassA castA = (MyClassA *)value;