NSMutableDictionary调用valueForKey获取字符串时是否会执行复制操作?
嘿,这个问题问得相当到位,刚好我对Foundation容器的内存管理行为比较熟悉,给你一步步拆解清楚:
1. 从字典取出NSString时,是不是副本?
答案是不是副本,你拿到的是字典中存储的那个NSString对象的直接引用。
不管是NSDictionary还是NSMutableDictionary,当你调用valueForKey:@"abc"(或者更常用的objectForKey:@"abc"/下标语法dict[@"abc"])时,字典只是把它持有的对象指针直接返回给你,不会做任何复制操作。字典在存储NSString时,只会通过ARC自动强引用它(MRC下是retain),不会主动创建副本。
2. 没有@property声明的影响?
这里要明确:字典的对象存储和类的@property完全是两回事。@property的copy/strong/weak修饰符是用来管理类实例变量的内存行为,而字典内部的对象引用管理是由容器自己处理的。所以不管有没有@property,你从字典里取出对象的逻辑都是一样的——返回原对象的引用,和复制无关。
3. 如果值是NSMutableString,会有什么区别?
区别非常大!因为你取出的是可变对象的直接引用,这意味着你可以直接修改这个对象,而且修改后的结果会直接同步到字典里——因为字典里存储的就是这个可变对象本身。举个代码例子:
NSMutableString *mutableStr = [NSMutableString stringWithString:@"original"]; NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithObject:mutableStr forKey:@"abc"]; // 取出可变字符串并修改 NSMutableString *fetchedStr = [dict valueForKey:@"abc"]; [fetchedStr appendString:@" modified"]; // 打印字典里的值,会看到修改后的结果 NSLog(@"dict[@\"abc\"] = %@", dict[@"abc"]); // 输出:dict["abc"] = original modified
而如果是不可变的NSString,你没法修改它的内容,自然不会出现这种“牵一发而动全身”的情况。
4. 数组调用valueForKey:@"abc"的场景
当你对数组调用valueForKey:@"abc"时,这是KVC的集合操作:数组会遍历每个元素(你的场景里是NSDictionary实例),对每个元素调用valueForKey:@"abc",然后把所有结果收集成一个新的NSArray返回。
这里每个元素的获取逻辑和单独从字典取是一致的:
- 如果原字典里的
@"abc"对应NSString,数组里的元素就是原NSString的引用; - 如果是NSMutableString,数组里的元素就是原可变字符串的引用,修改数组里的这个字符串,对应的字典里的值也会跟着变。
额外建议
如果你想避免意外修改字典里的内容(比如担心不小心修改了取出的可变字符串),可以自己手动创建副本:
// 取出后做不可变copy,得到和原对象无关的新字符串 NSString *safeStr = [[myDictionary valueForKey:@"abc"] copy];
内容的提问来源于stack exchange,提问作者Trygve

