对象是 NSMutableDictionary 的子类,但在 mutate 方法(NSJSONSerialization)上崩溃

Posted

技术标签:

【中文标题】对象是 NSMutableDictionary 的子类,但在 mutate 方法(NSJSONSerialization)上崩溃【英文标题】:Object is subclass of NSMutableDictionary but crashes on mutate method (NSJSONSerialization) 【发布时间】:2014-07-02 22:47:43 【问题描述】:

运行此代码:

NSData *jsonData = [@"\"foo\":\"bar\"" dataUsingEncoding:NSUTF8StringEncoding];
id result = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
if ([result isKindOfClass:[NSMutableDictionary class]])

    NSMutableDictionary *dict = (NSMutableDictionary *)result;
    [dict setObject:@"foo" forKey:@"baz"];

你会从setObject 调用中得到这个异常:

* 由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“-[__NSCFDictionary setObject:forKey:]: mutating method sent to immutable object”

使用***字典对象解析 JSON 时,NSJSONSerialization 返回一个不可变的字典对象,表示它是一个 NSMutableDictionary,但在您尝试对其进行变异时抛出异常。

我知道我可以通过NSJSONReadingMutableContainers 选项来获取可变容器。但是引擎盖下发生了什么?为什么一个类说它是一个可变的子类,然后表现得好像它是不可变的?

编辑以澄清问题:我知道有几种方法可以使此代码正常工作。我可以在结果上调用mutableCopy 或使用NSJSONReadingMutableContainers 选项来获得可变结果。这里的重点是一个对象说它的类型是NSMutableDictionary 或其子类,然后当我尝试对其进行变异时它会导致崩溃。为什么会存在这种行为?它应该被认为是一个错误吗?或者这是 Objective-C 运行时的一些预期怪癖?

【问题讨论】:

我没有继承 NSDictionaryNSMutableDictionary 您在问题标题中混淆了“子类”和“实例”。 查看 Apple 的 Concepts in Objective-C Programming 指南中关于 Object Mutability 的部分。其中一个子标题为:使用返回类型,而不是自省。下面是一段摘录:“[Y] 你不应该根据类成员资格对对象是否可变做出假设。您的决定应该完全由出售对象的方法的签名说明其可变性的内容来指导。” 类集群可能看起来很奇怪,但它们允许 Apple 的工程师对频繁使用的类进行一些非常有价值的性能优化。 NSJSONSerialization 的返回类型,在没有 mutable 选项的情况下,总是不可变的。 【参考方案1】:

因为您尝试在 result(您的 NSCFDictionary -- 不可变)而不是 dict(您的演员 NSMutableDictionary)上设置键对象。

请尝试:

NSData *jsonData = [@"\"foo\":\"bar\"" dataUsingEncoding:NSUTF8StringEncoding];
id result = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
if ([result isKindOfClass:[NSDictionary class]])

    NSMutableDictionary *dict = [result mutableCopy];
    [dict setObject:@"foo" forKey:@"baz"];

当您尝试确定它是否适合 NSMutableDictionary 类时,结果传递为 true 的原因是,NSCFDictionary 是一个同时匹配 NSDictionary 和 NSMutableDictionary 的类集群。 (见What is an NSCFDictionary?)。

本质上(在 if 语句中),您实际上根本没有检查 NSCFDictionary 是否可变——您只是检查它是否是 NSMutableDictionary 的子类(或成员类)。

【讨论】:

但他为什么要参与进来呢?那么它应该已经是一个 NSMutableDictionary。 是同一个对象,所以没关系。但我会更新这个问题来解决这个奇怪的问题。 我正在编辑这些详细信息 - 请参阅更新 如果我改用dict,它仍然会崩溃并出现相同的异常。 强制转换运算符无法在运行时更改对象的类型,因此示例中的强制转换不会使代码的行为与以前有任何不同。相反,您向原始字典发送 mutableCopy 消息,并使用可变实例。

以上是关于对象是 NSMutableDictionary 的子类,但在 mutate 方法(NSJSONSerialization)上崩溃的主要内容,如果未能解决你的问题,请参考以下文章

试图在 NSMutableDictionary 中设置 Number 类型的对象并得到奇怪的警告

NSMutableDictionary 按顺序添加对象问题

从 NSMutableDictionary 中删除对象

使用 NSMutableDictionary 将对象保存到数组

对值可能包含重复项的自定义对象的 NSMutableDictionary 进行排序

释放数组时 NSMutableDictionary 中使用的对象的内存会发生啥?