*

NSDictionary/NSMutableDictionaryの”valueForKey:”と”objectForKey:”の違い

公開日: : 最終更新日:2014/05/20 Tips , , ,

表題にある通り、似ていて混同する(そして、取り違えても「たいてい」普通に動作する)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というメソッドはない)。そこは注意が必要です。

関連記事

バージョンアップ版があることを通知する

拍子抜けするほど簡単だったので、「アプリのバージョンアップの通知をどうするか」という話についてTip

記事を読む

StoryboardからPopoverを作っても位置合わせコードは必要

これだけだとわかりづらいんですが。 XcodeのStoryboardから、Segueを"Prese

記事を読む

Size ClassとUIViewController.view

タイトルの件でちょっとハマったことがある上に、特に日本語情報もStackOverflowからも情報が

記事を読む

AVAudioPlayerの初回再生遅延

ちょっとした効果音の再生にAVAudioPlayerを使っているのですが、どうも初回の効果音ロードの

記事を読む

Xcode8で余計なログを抑制する

Xcode8に更新してから、妙にログが増えたので、NSLogで明示したのと実行時エラー以外のログを吐

記事を読む

CALayerの管理

CALayerはNSKeyValueCodingを実装している(Core Animation Ext

記事を読む

コードでiPhone6 Plusを識別する簡単な方法

表題の通り、コードでiPhone6 Plusを識別する簡単な方法です。 iPhone6 Plu

記事を読む

Bitbucket課題の記法サンプル

Bitbucketの課題、およびwikiで利用できる記法についての記事です。 利用できる記法に

記事を読む

UISegmentedControlに見栄え良く背景色を設定する

UISegmentedControlをiOS7で使おうとすると、基本の背景色が「透明」になりますが、

記事を読む

Bitbucketの画面の日本語化/コミット時に同時プッシュを行う

前回の記事の続きで、Bitbucketの画面の日本語化と、Xcodeでコミットするときに同時にpus

記事を読む

Xcode10からのimage literal / color literalの指定方法

アセットにあるUIImageの直接指定が可能なImage litera

Xcode11でのバージョン番号の取得方法

Xcode11になってバージョン番号をスクリプトから得て自動で更新する

GoogleスプレッドシートからCSVにエクスポートしたデータを得る方法

情報がない中さんざ苦労しましたが、Googleスプレッドシート(の最初

画面回転をしたあとに、UITextViewの先頭を表示する

回転によりサイズが可変するUITextViewで、縦向き(ポートレイト

NXDrawKitを導入してみる

ACEDrawingViewがObj-Cで書かれていて、いまいちメンテ

→もっと見る

    PAGE TOP ↑