NSKeyedUnarchiver - try / catch需要? [英] NSKeyedUnarchiver - try/catch needed?

查看:142
本文介绍了NSKeyedUnarchiver - try / catch需要?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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

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

NSDictionary * dictionary;

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

问题是(如文档 + unarchiveObjectWithData :如果 NSData 不包含有效的归档,则引发 NSInvalidArchiveOperationException p>

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

现在有两个问题:


  1. 为什么不 + unarchiveObjectWithData :只返回 nil 编辑: NSError ** )如果档案无效(这似乎不符合灾难性的条件) c或不可恢复的错误)。

  2. 上面的模式是否正确(使用 @try )?我没有发现没有方法可以预先检查数据是否包含有效的归档,并且没有发现使用委托协议来处理这种情况的可能性。 Antyhing我忽略了?

请注意,上面的代码当然可以工作,我只是想知道它的最佳做法。苹果公司生产的NSKeyedArchiver b $ b

解决方案

它们控制执行的代码,而执行 unarchiveObjectWithData 时,它们也可以在异常处理期间控制资源管理(这是Objective-C中的异常背后的麻烦来源)。 p>

如果他们可以保证在您调用 unarchiveObjectWithData:之间,并且它们引发异常的代码点不是外号(既不是第三方也不是你的应用程序的代码)在理论上可以安全地使用异常,只要调用代码正确地清理。



问题是这种假设可能不是这样:通常使用 NSKeyedArchiver 序列化自定义对象。通常,自定义类实现 initWithCoder:来读取类数据(通过使用归档程序的方法,如 decodeObjectForKey:)。 / p>

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



有关您的问题:




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





@try模式是否正确?


上述注意事项是正确的。 如果 Apple的代码正确处理异常情况,您自己的序列化代码也同样可以使用@try模式。



尽管如此,实现完全正确是非常困难的。您必须确保所有执行的代码都知道异常并且正确清理。



ARC,例如,不会对本地变量和临时对象执行异常清理(您必须启用 -fobjc-arc-exceptions 这样做)



另外,没有关于@synthesized属性的访问器的异常安全性的文档(当 atomic 他们可能会泄漏一个锁)。



结论:



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



所有这些都得出结论。如果要在加载可能损坏的归档文件时正确处理错误,然后继续正常执行:不要使用 NSKeyedArchiver


As I understand, the use of @try/@catch blocks is discouraged, because exceptions should only be thrown at unrecoverable, catastrophic errors (refer to this discussion with a nice answer by @bbum: Exception Handeling in iOS).

So I looked through my code and found a @try/@catch block that I don't know how to get rid of:

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

NSDictionary *dictionary;

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

The problem is that (as stated in the documentation) +unarchiveObjectWithData: raises an NSInvalidArchiveOperationException if the NSData doesn't contain a valid archive.

Since the data is provided by a file the user chose, it is not guaranteed that it contains a valid archive, and thus the application would crash if a incorrect file was chosen.

Now two questions:

  1. Why doesnt +unarchiveObjectWithData: just return nil (Edit: and an NSError**) if the archive is not valid (this doesn't seem to qualify as a catastrophic or unrecoverable error).
  2. Is the pattern above correct (using @try)? I have found no method that lets us check if the data contains a valid archive beforehand and have found no possibility to handle this case using the delegate protocol. Antyhing I overlooked?

Note that the code above of course works, I just wonder if its the best practice.

解决方案

NSKeyedArchiver is built by Apple. They control the code that is performed while unarchiveObjectWithData: executes so they also control resource management during exception handling (which is the source of trouble behind exceptions in Objective-C).

If they can guarantee that in between your call to unarchiveObjectWithData: and the point in code where they raise the exception is no foreign code (neither third party nor your app's code) it is in theory possible to safely use an exception, as long as calling code takes care of cleaning up correctly.

The problem is that this assumption might not be the case: It is common to use NSKeyedArchiver to serialize custom objects. Usually the custom class implements initWithCoder: to read the classes data (by using the archiver's methods like decodeObjectForKey:).

If the archiver throws an exception in one of these methods there's no way to fix resource handling for the archiver. The exception will be thrown through the custom object's initWithCoder:. The archiver does not know if there's more stuff to clean up than the deserialized objects. So in this scenario the occurrence of the exception means that the process is in a dangerous state and unwanted behavior may result.

Regarding your questions:

Why doesn't [NSKeyedArchiver use proper Cocoa error handling]?

Only the Apple engineers who built the archiver know. My guess is that exception handling and keyed archiving were built at roughly the same time (around 2001) and at that point it wasn't yet clear that exception handling would never be a first class citizen in Objective-C.

Is the @try pattern correct?

With the limitation of the caveats described above it is correct. If Apple's code handles the exception cases properly and your own serialization code does the same the @try pattern might be correct.

It is very difficult to achieve full correctness, though. You'd have to make sure all executed code is aware of the exceptions and does cleanup correctly.

ARC, for instance, does no exception cleanup for local variables and temporary objects by default (you would have to enable -fobjc-arc-exceptions to do this).

Also, there's no documentation on exception safety of the accessors of @synthesized properties (when atomic they might leak a lock).

Conclusion:

There are a myriad of subtle ways of how exceptions can break stuff. It is difficult and requires in depth knowledge of the implementation of all involved parts to build exception safe code in Objective-C.

All this leads to the conclusion. If you want to handle errors gracefully while loading possibly corrupted archives and continue normal execution afterwards: Do not use NSKeyedArchiver.

这篇关于NSKeyedUnarchiver - try / catch需要?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆