*

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というメソッドはない)。そこは注意が必要です。

関連記事

WSCoachMarksViewをSwiftで使う場合の手順

Objective-Cで記述されてるライブラリ全般に当てはまることもあるんですが、個別でやることも含

記事を読む

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

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

記事を読む

iOS8のシミュレーターでLocalizationのテストをする

iOS8.1のシミュレーターでは、従前できていたシミュレーター内での「設定」(Setting)からの

記事を読む

CoreDataのソートとsectionNameKeyPathの関係

CoreDataをフェッチするときに、indexPathのsectionにできるsectionNam

記事を読む

メソッドの呼び出し元を調べる

特定のメソッドに関して、そのメソッドをコールしているメソッド群を調べるための手順。 (1)調べたい

記事を読む

NSStringのフォーマット書式

Appleのページになく、IEEEのprintfフォーマットのページにあるので、C言語と親しくしてな

記事を読む

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

回転によりサイズが可変するUITextViewで、縦向き(ポートレイト)から横向き(ランドスケープ)

記事を読む

User Defined Runtime Attributeについての覚書

UILabelに上下左右のパディングをつける方法を探して、結局「自分で以下のようなメソッドを持った拡

記事を読む

AVAudioPlayerの初回再生遅延

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

記事を読む

UISplitViewControllerの仕切り線の色を設定

UISplitViewControllerで、iPadを横向き(Landscape)にしたときの2つ

記事を読む

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

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

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

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

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

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

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

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

NXDrawKitを導入してみる

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

→もっと見る

    PAGE TOP ↑