Subscribed unsubscribe Subscribe Subscribe

NSDictionaryのキーは変えられない

NSDictionaryのkeyは重複してはいけない。
じゃあ、インスタンスを作った後にキーを変更したりもできないのか?
「keyとvalueにそれぞれオブジェクトが格納されているだけ」と考えると、keyに可変なインスタンスを入れておけば格納された後に変更することも出来そうな気がする。
となるとNSDictionaryのインスタンスメソッド - (NSArray *)allKeys で返ってきたものをいじることができればいいワケだ。
というわけで試してみようと思った。

#import <Foundation/Foundation.h>

int main(int argc, char *argv[])
{
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    NSMutableString *key1 = [NSMutableString stringWithString:@"hoge"];
    NSMutableString *key2 = [NSMutableString stringWithString:@"fuga"];
    NSMutableString *key3 = [NSMutableString stringWithString:@"piyo"];
    NSArray *keys = [NSArray arrayWithObjects:key1, key2, key3, nil];

    NSString *value1 = @"foo";
    NSString *value2 = @"bar";
    NSString *value3 = @"baz";
    NSArray *values = [NSArray arrayWithObjects:value1, value2, value3, nil];

    NSDictionary *dict = [NSDictionary dictionaryWithObjects:values forKeys:keys];
    NSArray *allkeys = [dict allKeys];

    NSLog(@"%p, %@", keys, [keys description]);
    NSLog(@"%p, %@", allkeys, [allkeys description]);

    [pool release];

    return 0;
}

NSArrayのインスタンスからkeyを登録して、NSArrayを返すメソッドを呼んでるのだから、同じものが返ってきてくれる可能性も?と淡い期待を抱いていたけど…結果は

$ ./a.out
2008-12-03 23:41:44.102 a.out[958:10b] 0x1059d0, (
    hoge,
    fuga,
    piyo
)
2008-12-03 23:41:44.104 a.out[958:10b] 0x106950, (
    fuga,
    piyo,
    hoge
)

うん、明らかに違うっぽい。
じゃあ中身はどうなってるんだ?NSMutableStringで作ったkeyなんだからそれがそのまま格納されているわけじゃないのか?

...
    NSLog(@"%p, %@", keys, [keys description]);
    for (id key in keys) {
        NSLog(@"%p, %@, %@", key, key, [key className]);
    }
    NSLog(@"%p, %@", allkeys, [allkeys description]);
    for (id key in allkeys) {
        NSLog(@"%p, %@, %@", key, key, [key className]);
    }
...

中身を見てみる。

$ ./a.out
2008-12-03 23:56:47.101 a.out[1092:10b] 0x1059d0, (
    hoge,
    fuga,
    piyo
)
2008-12-03 23:56:47.103 a.out[1092:10b] 0x105910, hoge, NSCFString
2008-12-03 23:56:47.104 a.out[1092:10b] 0x105950, fuga, NSCFString
2008-12-03 23:56:47.105 a.out[1092:10b] 0x105990, piyo, NSCFString
2008-12-03 23:56:47.106 a.out[1092:10b] 0x106950, (
    fuga,
    piyo,
    hoge
)
2008-12-03 23:56:47.107 a.out[1092:10b] 0x106790, fuga, NSCFString
2008-12-03 23:56:47.108 a.out[1092:10b] 0x1067a0, piyo, NSCFString
2008-12-03 23:56:47.109 a.out[1092:10b] 0x106780, hoge, NSCFString

うぉ、そうか、NSStringでもNSMutableStringでもないのか。
しかも全然別のインスタンスだ…
ということは、appendStringなんてできない…?

...
    NSLog(@"%p, %@", keys, [keys description]);
    [[keys objectAtIndex:0] appendString:@"++"];
    NSLog(@"%p, %@", keys, [keys description]);
...

おそるおそる実行。

$ ./a.out
2008-12-03 23:49:25.697 a.out[1033:10b] 0x1059d0, (
    hoge,
    fuga,
    piyo
)
2008-12-03 23:49:25.699 a.out[1033:10b] 0x1059d0, (
    "hoge++",
    fuga,
    piyo
)

おぉ、これは大丈夫なのか!
じゃあallKeysで戻ってきたNSArrayの方は…?

...
    NSLog(@"%p, %@", allkeys, [allkeys description]);
    [[allkeys objectAtIndex:0] appendString:@"++"];
    NSLog(@"%p, %@", allkeys, [allkeys description]);
...

えいっ!

$ ./a.out
2008-12-03 23:51:30.009 a.out[1048:10b] 0x106950, (
    fuga,
    piyo,
    hoge
)
2008-12-03 23:51:30.012 a.out[1048:10b] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attempt to mutate immutable object with appendString:'
2008-12-03 23:51:30.013 a.out[1048:10b] Stack: (
    2488357195,
    2443800123,
    2488356651,
    2488356714,
    2491153866
)

あ。死んだ。
やっぱりこっちに格納されているのは可変なオブジェクトじゃないのかー。
とにかくNSDictionaryのallKeysメソッドで返ってきたものは、生成時に渡したものと同じインスタンスが返ってくるわけではないらしい。
NSStringとかNSMutableStringっていう実体があるわけじゃないから、中身がどうなっているのかはよく分からない、というところに起因しているのかな。このへんはまだよく分かってない。
こたつつきみかん » 見えないクラス群
ダイナミックObjective-C (38) Toll-free bridge(1) - 変換コスト0のブリッジ | マイナビニュース
ダイナミックObjective-C (63) デザインパターンをObjective-Cで - Factory Method (3) | マイナビニュース
http://developer.apple.com/jp/documentation/Cocoa/Conceptual/CocoaFundamentals/CocoaObjects/chapter_3_section_9.html
とかをじっくり読めば理解できるかな…?
Objective-C、奥が深いわー。もっとしっかり勉強しなくては。