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というメソッドはない)。そこは注意が必要です。
関連記事
-
-
viewDidLoadとviewDidLayoutSubviewsのタイミング
真実の記事にある「コードでiPhone6を識別する」話とほんのちょっとだけ繋がっている話で、view
-
-
iOS9でのNSFetchedResultsContollerDelegateの挙動
ハマったので短いながらメモしたいと思います。 NSFetchedResultsContollerD
-
-
GoogleスプレッドシートからCSVにエクスポートしたデータを得る方法
情報がない中さんざ苦労しましたが、Googleスプレッドシート(の最初のシート)をCSVとしてエクス
-
-
iOS7でのPopover内サイズの指定
UIPopover内で表示する場合のViewControllerのコンテンツサイズ設定に関してのメモ
-
-
コードでiPhone6 Plusを識別する簡単な方法
表題の通り、コードでiPhone6 Plusを識別する簡単な方法です。 iPhone6 Plu
-
-
CALayerの管理
CALayerはNSKeyValueCodingを実装している(Core Animation Ext
-
-
plistファイルに色を入れる
plistファイル中にUIColorを入れたい!という場合。普通RGBAの値を数値で入れるとかするけ
-
-
AdMob組み込み覚書
書籍ではコレがかなり参考になるのでご紹介。 表題の通りで、組み込みで要注意っぽい部分だけを
-
-
UITableViewCellのロングタップ
UITableViewCellをロングタップしたときに、タップされたセルを認識しつつメニューを出した
