0

我遇到了一个非常奇怪的问题,经过几个月的测试,当我即将提交应用程序时,它恰好弹出。

我有以下方法,它需要一些 JSON 数据并将其转换为字典:

NSError *e;
NSMutableDictionary *result= [NSJSONSerialization JSONObjectWithData:jsonData 
                              options:NSJSONReadingMutableContainers error:&e];
if (e != nil) return nil;

在过去的几个月里,这种方法一直在不停地使用,绝对没有问题。但就在今天,它刚刚停止工作。它现在总是导致错误(没有描述;只是一个非零错误)。

事实证明,我所要做的就是解决这个问题NSError *e = nil;。我认为这样做只是一个好习惯,而不是绝对关键。这让我害怕。我想知道我在我的代码中还有多少次这样做。谁能解释可能发生的事情?

另外,我正在使用 ARC,我想这让发生这种情况变得更加奇怪。

4

3 回答 3

4

您的代码是错误的,您的修复也是如此。你需要做的是说

if (result == nil) {
    // an error occurred, and the NSError* variable can now be consulted
}

如果result是除 之外的任何内容nil,那么您不得对 的内容做出任何e假设。


这里的基本原因是具有NSError**返回值的 API 不需要在该位置放置任何内容,除非 API 返回错误。通常这意味着在非错误情况下,它们根本不会修改值,因此您之前在e变量中拥有的东西就是您之后拥有的东西。如果您的代码是在没有 ARC 的情况下编译的,那么您的e变量将包含来自堆栈的垃圾。在 ARC 下,它将被初始化为nil,但我猜你不在 ARC 下,原因我会讲到。

然而,它比这更复杂。即使该方法没有返回错误,它也可能仍然修改了该NSError**值。一个简单的例子是,如果这个方法调用另一个方法,将相同的方法传递给NSError**那个方法,然后从错误中恢复并返回一个成功值。然而,第二种方法可能已经NSError*用不再有效的错误填充了您的变量。

现在,我认为您的代码不是 ARC 的原因是,据我所知,这些天所有的 Cocoa API 都在努力修改NSError**值,除非发生错误。这符合一两年前制定的新准则(它是在 2011 年初或 2012 年初,我忘记了),即具有NSError**参数的方法只应在错误情况下修改它。这是为了让说的代码NSError *e = nil; [foo callAPIWithError:e]; if (e) ...能够工作,即使这实际上并不遵循NSErrorAPI 的规则,纯粹是作为一个实际问题,在面对不正确的代码时更有弹性。而且由于 ARC 取消了具有对象类型的所有自动变量,因此您的崩溃表明该变量e没有被取消,因此您不在 ARC 中。

然而,尽管我刚刚在上一段中说过,你仍然不应该假设任何NSError启用了 - 的 APINSError在没有抛出错误的情况下都会单独保留该值。当前实现此类 API 的指南确实说这应该是正确的,但这不是硬性要求。在这些指南之前编写的任何代码可能不会以这种方式运行,并且在指南之后编写的任何代码都可能会忽略它们。调用NSError启用的 API 的规则继续规定必须查阅 API 的返回值,并且NSError*只有在返回值指示发生错误时才能观察变量。

于 2012-12-12T19:37:08.533 回答
2

让我一步一步解释错误的原因:

NSError *e; //some garbage non-nil value may be here

NSMutableDictionary *result= [NSJSONSerialization JSONObjectWithData:jsonData 
                              options:NSJSONReadingMutableContainers error:&e]; // no error, e is untouched, garbage is still here

if (e != nil) return nil; // there was no error but because of garbage we think there was one

因此,首先您需要检查结果值或将 e 变量设置为 nil(首选第一个)。

于 2012-12-12T19:38:10.937 回答
0

该方法的参考资料说:

返回值 data 中 JSON 数据的 Foundation 对象,如果发生错误,则返回 nil。

所以使用返回值来检测错误,并且只有当你想要详细查看错误对象时。

于 2012-12-12T19:37:15.207 回答