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というメソッドはない)。そこは注意が必要です。
関連記事
-
UITableViewCellのロングタップ
UITableViewCellをロングタップしたときに、タップされたセルを認識しつつメニューを出した
-
NSAttributedStringをローカライズする方法
NSAttributedStringをLocalizeするうまい方法はないものかと探したら、これが手
-
UIAutomation関連覚書
割りと忘れがちなので、自分用備忘録を兼ねて、UIAutomationに関連するtips的なものをまと
-
AdMob組み込み覚書
書籍ではコレがかなり参考になるのでご紹介。 表題の通りで、組み込みで要注意っぽい部分だけを
-
Xcode10からのimage literal / color literalの指定方法
アセットにあるUIImageの直接指定が可能なImage literalですが、気がついたらアセット
-
UILabelの改行をInterfaceBuilderから入力する
UILabelの行数は任意に設定できて、「0」を指定すると行数を限定しないで(UILabelのbou
-
RealmのList
とArray の相互変換によるマップ Realm+ObjectMapperを使って、RealmのオブジェクトをJSON変換してたのですが
-
UISplitViewControllerの仕切り線の色を設定
UISplitViewControllerで、iPadを横向き(Landscape)にしたときの2つ
-
bitbucketをXcode5で使用する
bitbucketは無償で5人までの少人数チーム用のクローズド(非公開)リポジトリを作成できるため、