内容简介:使用环境:描述: 使用
使用环境:
Xcode 10.1 Swift 4.0
描述:
使用 NSCoding
进行 archive
和 unarchive
归档。旧的工程名叫 A, 新的工程名叫 B。A 曾经在设备上运行过,并使用 NSUserDefault
针对序列化后的 Data
进行持久化保存。
当更换工程名后,B 在运行时从 userDefault
中取出这个 NSData
来做解档报错了
'NSInvalidUnarchiveOperationException', reason: '*** -[NSKeyedUnarchiver decodeObjectForKey:]: cannot decode object of class (MyProject.MyClass) for key (root); the class may be defined in source code or a library that is not linked'
自测后发现
测试过发现了如下问题:
-
使用
OC创建工程,变更工程名之后,调用[NSkeyedUnarchiver unarchiveobject]是没有问题的。 -
在
Swift上发现一定会造成闪退 - ios 11 和 ios 12之后的版本,unarchive 分别添加了一个 函数,用来配合解档失败时候的处理(try catch)
- 看了我们使用的第三方登录/分享的平台,他们也有使用到 归档和解档。只是用的是 OC
原因
When you use the @objc(name) attribute on a Swift class, the class is made available in Objective-C without any namespacing. As a result, this attribute can also be useful when you migrate an archivable Objective-C class to Swift. Because archived objects store the name of their class in the archive, you should use the @objc(name) attribute to specify the same name as your Objective-C class so that older archives can be unarchived by your new Swift class.
——参考出处
何为命名空间
OC中没有命名空间的概念,在进行应用开发时,所有的代码和引用的静态库最终会被编译到同一个域和二进制文件中。这样当两个类名重复的时候,就会导致编译冲突和失败。这也就是为什么我们在写OC代码的时候要添加类名前缀的原因。比如苹果本身保留的前缀UI和NS 还有各个系统框架的前缀AF、SD等,这样做可以大大降低引起冲突的几率,但是风险仍然存在,如果你在项目中同时加载进两个不同的库,而这两个库都分别引用了同一个第三方库而没有修改名字,这样就会发生冲突。 Swift由于命名空间的存在,既是两个名称相同的类,只要他们来自不同的命名空间就不会产生编译时的冲突。 "在 Swift 中,由于可以使用命名空间了,即使是名字相同的类型,只要是来自不同的命名空间的话,都是可以和平共处的。
——参考出处
结论
由于 Swift
的机制原因,创建的类都会带上 命名空间
(简单的理解就是工程名,在 Info.plist
中查看源码,看到的那个 CFBundleName
就是命名空间,实际上就是工程名)。当我们变更了工程的同事,也就意味着命名空间跟着变了。在上一个工程中归档自定义类时,带上了旧工程的命名空间。因此在新工程做解档事,找不到对应的命名空间,早场了 crash
解决方案:
预防方法
如果我们在 Swift
中针对一个自定义类使用 NSKeyedUnarchiver
进行归档,那么这个自定义类建议定义为 oc
的类,在创建类的时候, class
前要加上 @objc
关键字,这样累就不会带上命名空间了。
@objc class MyClass: NSObject, NSCoding {
//... 略去,下面要去实现 NSCoding 的 decode 和 encode delegate
}
复制代码
规避方法
如果你之前已经发布了一个 没有使用过 @objc
关键字的类,这时候新发的版本肯定无法将这个已经存在 NSUserDefault
中的类变为不带命名空间。意味着即使升级版本,也会造成崩溃。
那么怎么办呢?
我看过一些社交类的第三方应用提供的 sdk
,如 facebook, vk, twitter
等等,他们的归档对象类型目前还是用 OC
的。但是我在 vk
的代码中看到了 try catch
查看了一下 NSKeyedUnarchiver
的 swift
官方 API
,发现了两个带有 throws
的 API
,但是他们支持的版本都在 11.0
之后。
@available(OSX 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) @nonobjc public static func unarchivedObject<DecodedObjectType>(ofClass cls: DecodedObjectType.Type, from data: Data) throws -> DecodedObjectType? where DecodedObjectType : NSObject, DecodedObjectType : NSCoding @available(OSX 10.13, iOS 11.0, watchOS 4.0, tvOS 11.0, *) @nonobjc public static func unarchivedObject(ofClasses classes: [AnyClass], from data: Data) throws -> Any? 复制代码
因为大多数的 app
应用都需要从 ios 8.0
开始支持,所以最好的方法是我们自己去添加一个 try catch
处理解档失败时候的崩溃。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- PyMiner 开源协议变更为 LGPL,技术变更为 PySide2
- 该崩溃的时候就崩溃吧,至少写程序应该是这样
- 使用JGit获取变更细节
- 每日获取变更的CVE漏洞
- Raft 成员变更的工程实践
- 生产变更的几点感悟
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Beginning Apache Struts
Arnold Doray / Apress / 2006-02-20 / USD 44.99
Beginning Apache Struts will provide you a working knowledge of Apache Struts 1.2. This book is ideal for you Java programmers who have some JSP familiarity, but little or no prior experience with Ser......一起来看看 《Beginning Apache Struts》 这本书的介绍吧!