NSDictionary/NSMutableDictionaryの”valueForKey:”と”objectForKey:”の違い
公開日:
:
最終更新日:2014/05/20
Tips KVC, NSDictionary, NSMutableDictionary, Xcode
表題にある通り、似ていて混同する(そして、取り違えても「たいてい」普通に動作する)NSDictionary(NSMutableDictionary)のobjectForKeyとvalueForKey(および、setValue:forKey:とsetObject:forKey:)の違いについて、覚書も兼ねてまとめてみました。
結論からいくと、NSDictionary的に正しいのsetObject:forKey:とobjectForKey:の方です。
setValue:forKey:、valueForKey:は本来Key-Value-Coding用ですが、NSDictionaryはたいていうまく行くようにメソッドを実行してくれてるので、「たいてい普通に動作する」というのが実際のようです。
実際の差異を確かめるために、以下のようなコードを使って動作を検証してみました。
NSString* Key_A = @"TEST";
NSString* Key_B = @"@TEST";
NSMutableDictionary* dic = [NSMutableDictionary dictionary];
[dic setValue:@"TEST VALUE" forKey:Key_A];
[dic setValue:@"@TEST VALUE" forKey:Key_B];
[dic setObject:@"TEST VALUE" forKey:Key_A];
[dic setObject:@"@TEST VALUE" forKey:Key_B];
NSLog(@"dic is %@", [dic description]);
NSLog(@"objectForKey:'%@' is %@", Key_A, [dic objectForKey:Key_A]);
NSLog(@"objectForKey:'%@' is %@", Key_B, [dic objectForKey:Key_B]);
NSLog(@"valueForKey:'%@' is %@", Key_A, [dic valueForKey:Key_A]);
NSLog(@"valueForKey:'%@' is %@", Key_B, [dic valueForKey:Key_B]);
このコードの実行ログはこうなりました。
dic is {
“@TEST” = “@TEST VALUE”;
TEST = “TEST VALUE”;
}
objectForKey:’TEST’ is TEST VALUE
objectForKey:’@TEST’ is @TEST VALUE
valueForKey:’TEST’ is TEST VALUE
*** Terminating app due to uncaught exception ‘NSUnknownKeyException’, reason: ‘[<__nsdictionarym 0x8c8bd90> valueForUndefinedKey:]: this class is not key value coding-compliant for the key TEST.’
最後の[dic valueForKey:Key_B]でNSUnknownKeyExceptionが発生していますが、Key_AについてはobjectForKey,valueForKeyが等価の結果を返しました。
また、NSMutableDictionaryのdescriptionでは、valueもobjectも区別している様子はありません。
そこでクラスリファレンスの当該メソッドの説明を見て、具体的な動作の違いを確認してみます。
setValue:forKeyのDiscussionには、このように書いてあります(日本語部分は自分の意訳です)。
This method adds value and key to the dictionary using setObject:forKey:, unless value is nil in which case the method instead attempts to remove key using removeObjectForKey:.
(setValue:forKey:メソッドは、setObject:forKey:メソッドを使って値をdictionaryに登録します。ただし、valueがnilの場合は例外的にremoveObjectForKey:を使ってdictionaryから値を取り除きます)
従って、valueがnilでない限りは、setValueはsetObjectと等価で、(Key-Value-Codingに対応する)valueに値は入りません。
検証コードでdescriptionとして表示されているのは、いずれも「object」だということです。このため、objectForKey:メソッドでは正常に値が取り出せるようです。
一方、valueForKey:メソッドのDiscussionでは、このような記載があります(先ほどと同様、日本語部分は自分の意訳です)。
If key does not start with “@”, invokes objectForKey:. If key does start with “@”, strips the “@” and invokes [super valueForKey:] with the rest of the key.
(keyの値が”@”から開始されていないなら、単純にobjectForKeyとして機能します。もし”@”から開始されている場合は、”@”を取り除いた値をkeyにして[super valueForKey:]メソッドを実行します)
この仕様のため、@”@TEST”というキーに対しては、[super valueForKey:@”TEST”]が実行されます。NSDictionaryの親クラスはNSObjectですが、NSObjectのvalueForKey:はKey-Value-Cordingのvalueを引っ張ってこようとするため、NSUnknownKeyExceptionが発生する、ということのようです。
このことからも、NSDictionary系ではsetObject:forKey:およびobjectForKey:のほうが本来想定されているメソッドだということがわかります。
ただ、キーにかかわらず全ての値をNSArrayで持ってくるメソッドはallValuesメソッドしかありません(allObjectsというメソッドはない)。そこは注意が必要です。
関連記事
-
-
UIButtonのタイトル変更時のチラつき抑制
久しぶりにXcode+Objective-Cでアプリを作っていたら、UIButtonのタイトルを動的
-
-
RealmのList
とArray の相互変換によるマップ Realm+ObjectMapperを使って、RealmのオブジェクトをJSON変換してたのですが
-
-
画面回転をしたあとに、UITextViewの先頭を表示する
回転によりサイズが可変するUITextViewで、縦向き(ポートレイト)から横向き(ランドスケープ)
-
-
メソッドの呼び出し元を調べる
特定のメソッドに関して、そのメソッドをコールしているメソッド群を調べるための手順。 (1)調べたい
-
-
iOS7でカメラ撮影時のステータスバーを消す
UIImagePickerControllerをカメラモードで表示するとステータスバーがカメラの一番
-
-
Bitbucket課題の記法サンプル
Bitbucketの課題、およびwikiで利用できる記法についての記事です。 利用できる記法に
-
-
bitbucketをXcode5で使用する
bitbucketは無償で5人までの少人数チーム用のクローズド(非公開)リポジトリを作成できるため、
-
-
バージョンアップ版があることを通知する
拍子抜けするほど簡単だったので、「アプリのバージョンアップの通知をどうするか」という話についてTip
-
-
App Storeのページにアプリケーションから直接アクセスする方法
去年(2013年)の秋ごろ、iOS7の出始めの時期に書かれたblog記事などを参考にすると、iOS6
-
-
Xcode10からのimage literal / color literalの指定方法
アセットにあるUIImageの直接指定が可能なImage literalですが、気がついたらアセット
