Swift中使用NSKeyedUnarchiver时如何避免闪退

1,449 阅读2分钟

最近遇到一个闪退

-[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (AppName.ClassModel) for key (NS.objects) because no class named "AppName.ClassModel" was found; the class needs to be defined in source code or linked in from a library (ensure the class is part of the correct target). If the class was renamed, use setClassName:forClass: to add a class translation mapping to NSKeyedUnarchiver

查了原因是因为组件化的时候把一个类从主工程移动到一个组件里面去了。结果导致上面的问题。

为什么移动一下类会这样?

原因是旧版代码用NSKeyedUnarchiver类的归档,使用了某个类ClassModel,如果新的代码把类ClassModel改了命名或者命名空间,那么就会报找不到类的问题,组件化就是把在主工程的类移动到组件里面去,命名空间变了。

解决方法也简单。添加多一行代码

NSKeyedUnarchiver.setClass(ClassModel.self, forClassName: "AppName.ClassModel")

这样给他重新绑定一下新的类就行。

但是这也太坑了吧,改名不是很普遍的做法吗?而且网上查了有些人改了主工程的名字也导致类似的问题。

核心还是因为命名空间。

有没有好一点的方法来解决他呢。难度只能等闪退的时候再来修复问题?毕竟开发的时候很多时候也不会用旧版的数据来验证新代码逻辑。

还真没有好方法,毕竟你归档的类,在代码中被改是很正常的操作。能不能归档的时候也把当时的类的命名空间等提前记住,默认给他注入一个class,做为开发规范?

比如

NSKeyedUnarchiver.setClass(ClassModel.self, forClassName: "AppName.ClassModel")
let list = NSKeyedUnarchiver.unarchiveObject(withFile: payLogFilePath) as? [ClassModel]

但这样移动类到组件里面也一样有问题。毕竟组件名字不叫AppName

最后想到方法可以这样

do {
    let list = try NSKeyedUnarchiver.unarchiveObject(withFile: payLogFilePath) as? [ClassModel]
} catch {
	print("error")
}

但是这样只是不闪退了,该出现的问题,还是一样出现。而且这样会让问题被掩盖,等于说出现了问题,但是技术开发人员不知道。

最后想到这样

do {
    let list = try NSKeyedUnarchiver.unarchiveObject(withFile: payLogFilePath) as? [ClassModel]
} catch {
    // 避免闪退,而且让bugly收集错误
    Bugly.reportException(withCategory: 6,
    name: "出错了",
    reason: "NSKeyedUnarchiver转模型的时候出错了",
    callStack: [],
    extraInfo: [:],
    terminateApp: false)
}

一方面避免了闪退,另一方面Bugly等日志平台也能收到错误提示。方便后面出问题的时候整改。

看来得有一个开发规范,当使用NSKeyedUnarchiver.unarchiveObject时,就应该用上面的try包住,并上传错误日志。