据我所知,不建议使用@try/@catch
块,因为exceptions
只应抛出不可恢复的灾难性错误(请参阅此讨论,并通过@bbum得到一个很好的答案: Exception Handeling in iOS)。
所以我查看了我的代码并找到了一个我不知道如何摆脱的@try/@catch
块:
NSData *fileData = [NSData dataWithContentsOfFile: ....];
NSDictionary *dictionary;
@try {
dictionary = [NSKeyedUnarchiver unarchiveObjectWithData: fileData];
}
@catch (NSException *exception) {
//....
}
@finally {
//...
}
问题是(如documentation中所述)+unarchiveObjectWithData:
如果NSInvalidArchiveOperationException
不包含有效存档,则会引发NSData
。
由于数据是由用户选择的文件提供的,因此无法保证它包含有效的存档,因此如果选择了错误的文件,应用程序将会崩溃。
现在有两个问题:
+unarchiveObjectWithData:
不返回nil
(编辑:和NSError**
)(这似乎不符合资格灾难性或不可恢复的错误)。@try
)?我找不到任何方法可以让我们事先检查数据是否包含有效的存档,并且发现无法使用委托协议处理这种情况。我忽略了Antyhing?请注意,上面的代码当然有效,我只是想知道它是否是最佳实践。
答案 0 :(得分:18)
iOS 9中添加了一个新方法NSKeyedUnarchiver现在返回错误:
夫特:
public class func unarchiveTopLevelObjectWithData(data: NSData) throws -> AnyObject?
目标-C:
+ (nullable id)unarchiveTopLevelObjectWithData:(NSData *)data error:(NSError **)error;
但是,这与以前版本的iOS不向后兼容,因此您需要check for framework availability。
答案 1 :(得分:9)
NSKeyedArchiver
由Apple制作。它们控制在unarchiveObjectWithData:
执行时执行的代码,因此它们还控制异常处理期间的资源管理(这是Objective-C中异常背后的麻烦来源)。
如果他们可以保证在您对unarchiveObjectWithData:
的调用和他们提出异常的代码中的点之间没有外国代码(既不是第三方也不是您的应用程序的代码),理论上 可以安全地使用异常,只要调用代码可以正确清理。
问题是这种假设可能不是这样的:通常使用NSKeyedArchiver
来序列化自定义对象。通常,自定义类实现initWithCoder:
来读取类数据(通过使用归档器的方法,如decodeObjectForKey:
)。
如果归档程序在其中一种方法中抛出异常,则无法修复归档程序的资源处理。异常将通过自定义对象initWithCoder:
抛出。归档程序不知道清理的内容是否比反序列化的对象更多。因此,在这种情况下,异常的发生意味着进程处于危险状态,并可能导致不需要的行为。
为什么[NSKeyedArchiver没有使用正确的Cocoa错误处理]?
只有建立归档程序的Apple工程师才知道。我的猜测是,异常处理和密钥存档大致在同一时间(大约在2001年)建立,此时尚不清楚异常处理永远不会成为Objective-C中的一等公民。
@try模式是否正确?
由于上述警告的限制,这是正确的。 如果 Apple的代码正确处理异常情况和您自己的序列化代码也是如此,@try模式可能是正确的。
但是,要实现完全正确是非常困难的。您必须确保所有执行的代码都知道异常并正确清理。 例如,对于局部变量和临时对象, ARC does no exception cleanup(您必须启用-fobjc-arc-exceptions
才能执行此操作)。
此外,没有关于@synthesized属性的访问者的异常安全性的文档(当atomic
他们可能泄漏锁定时)。
有许多微妙的方法可以解决异常如何破坏事物。很难并且需要深入了解所有相关部分的实现,以在Objective-C中构建异常安全代码。
这一切都导致了结论。如果您想在加载可能已损坏的档案时正常处理错误并在之后继续正常执行:请勿使用NSKeyedArchiver
。