NSKeyedUnarchiver - try/catch needed?

狂风中的少年 提交于 2019-12-03 02:36:51
Nikolai Ruhe

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.

Senseful

There was a new method added in iOS 9 to NSKeyedUnarchiver that now returns an error:

Swift:

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

Objective-C:

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

However, this is not backwards compatible with previous versions of iOS, so you will need to check for framework availability.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!