21

据我了解,@try/@catch不鼓励使用块,因为exceptions只应在不可恢复的灾难性错误中抛出(请参阅此讨论,@bbum 给出了一个很好的答案:iOS 中的异常处理)。

所以我查看了我的代码,发现了一个@try/@catch我不知道如何摆脱的块:

NSData *fileData = [NSData dataWithContentsOfFile: ....];

NSDictionary *dictionary;

@try {
   dictionary = [NSKeyedUnarchiver unarchiveObjectWithData: fileData];
}
@catch (NSException *exception) {
   //....
}
@finally {
  //...
}

问题是(如文档中所述)如果不包含有效存档,则会+unarchiveObjectWithData:引发一个。NSInvalidArchiveOperationExceptionNSData

由于数据是由用户选择的文件提供的,因此不能保证它包含有效的存档,因此如果选择了不正确的文件,应用程序将会崩溃。

现在有两个问题:

  1. 如果存档无效(这似乎不符合灾难性或不可恢复的错误),为什么+unarchiveObjectWithData:不只返回nilEdit:和 an )。NSError**
  2. 上面的模式是否正确(使用@try)?我没有发现任何方法可以让我们事先检查数据是否包含有效的存档,并且没有发现使用委托协议处理这种情况的可能性。我忽略了什么?

请注意,上面的代码当然有效,我只是想知道它是否是最佳实践。

4

2 回答 2

20

iOS 9 中向NSKeyedUnarchiver添加了一个新方法,该方法现在返回错误:

迅速:

public class func unarchiveTopLevelObjectWithData(data: NSData) throws -> AnyObject?

目标-C:

+ (nullable id)unarchiveTopLevelObjectWithData:(NSData *)data error:(NSError **)error;

但是,这与以前的 iOS 版本不向后兼容,因此您需要检查框架可用性

于 2015-10-27T23:11:38.753 回答
10

NSKeyedArchiver由苹果公司打造。它们控制执行时unarchiveObjectWithData:执行的代码,因此它们还控制异常处理期间的资源管理(这是 Objective-C 中异常的根源)。

如果他们可以保证在您的调用unarchiveObjectWithData:和他们引发异常的代码点之间没有外来代码(第三方和您的应用程序的代码都不是),那么理论上可以安全地使用异常,只要调用代码需要注意正确清理。

问题是这种假设可能并非如此:通常用于NSKeyedArchiver序列化自定义对象。通常自定义类实现initWithCoder:读取类数据(通过使用归档器的方法,如decodeObjectForKey:)。

如果归档器在其中一种方法中抛出异常,则无法修复归档器的资源处理。异常将通过自定义对象的initWithCoder:. 存档器不知道是否有比反序列化对象更多的东西要清理。因此,在这种情况下,异常的发生意味着进程处于危险状态,可能会导致不需要的行为。

关于你的问题:

为什么 [NSKeyedArchiver 不使用正确的 Cocoa 错误处理]?

只有构建存档器的 Apple 工程师知道。我的猜测是异常处理和键控归档是在大约同一时间(大约 2001 年)构建的,那时还不清楚异常处理永远不会成为 Objective-C 中的一等公民。

@try 模式是否正确?

由于上述警告的限制,它是正确的。如果Apple 的代码正确处理异常情况并且您自己的序列化代码执行相同的操作,则 @try 模式可能是正确的。

但是,要完全正确是非常困难的。您必须确保所有执行的代码都知道异常并正确进行清理。

例如,ARC默认情况下不会对局部变量和临时对象进行异常清理(您必须启用-fobjc-arc-exceptions才能执行此操作)。

此外,没有关于 @synthesized 属性的访问器的异常安全性的文档(当atomic它们可能泄漏锁时)。

结论:

异常如何破坏事物有无数种微妙的方式。在 Objective-C 中构建异常安全代码是困难的,并且需要深入了解所有相关部分的实现。

所有这些都导致了结论。如果您想在加载可能损坏的档案时优雅地处理错误并在之后继续正常执行:不要使用NSKeyedArchiver.

于 2013-06-25T15:19:21.653 回答