使用環境:ios
Xcode 10.1
Swift 4.0
描述: 使用 NSCoding
進行 archive
和 unarchive
歸檔。舊的工程名叫 A, 新的工程名叫 B。A 曾經在設備上運行過,並使用 NSUserDefault
針對序列化後的Data
進行持久化保存。 當更換工程名後,B 在運行時從userDefault
中取出這個NSData
來作解檔報錯了swift
'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'ruby
連接app
測試過發現了以下問題:框架
OC
建立工程,變動工程名以後,調用 [NSkeyedUnarchiver unarchiveobject]
是沒有問題的。Swift
上發現必定會形成閃退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.函數
—— 參考出處post
何爲命名空間
OC中沒有命名空間的概念,在進行應用開發時,全部的代碼和引用的靜態庫最終會被編譯到同一個域和二進制文件中。這樣當兩個類名重複的時候,就會致使編譯衝突和失敗。這也就是爲何咱們在寫OC代碼的時候要添加類名前綴的緣由。好比蘋果自己保留的前綴UI和NS 還有各個系統框架的前綴AF、SD等,這樣作能夠大大下降引發衝突的概率,可是風險仍然存在,若是你在項目中同時加載進兩個不一樣的庫,而這兩個庫都分別引用了同一個第三方庫而沒有修更名字,這樣就會發生衝突。 Swift因爲命名空間的存在,既是兩個名稱相同的類,只要他們來自不一樣的命名空間就不會產生編譯時的衝突。 "在 Swift 中,因爲可使用命名空間了,即便是名字相同的類型,只要是來自不一樣的命名空間的話,都是能夠和平共處的。測試
—— 參考出處 —— 喵神寫的命名空間的文檔this
因爲 Swift
的機制緣由,建立的類都會帶上 命名空間
(簡單的理解就是工程名,在 Info.plist
中查看源碼,看到的那個 CFBundleName
就是命名空間,實際上就是工程名)。當咱們變動了工程的同事,也就意味着命名空間跟着變了。在上一個工程中歸檔自定義類時,帶上了舊工程的命名空間。所以在新工程作解檔事,找不到對應的命名空間,早場了 crashspa
若是咱們在 Swift
中針對一個自定義類使用 NSKeyedUnarchiver
進行歸檔,那麼這個自定義類建議定義爲 oc
的類,在建立類的時候, class
前要加上 @objc
關鍵字,這樣累就不會帶上命名空間了。
@objc(MyClass) class MyClass: NSObject, NSCoding {
//... 略去,下面要去實現 NSCoding 的 decode 和 encode delegate
}
複製代碼
若是咱們就是想要保證 swift
的命名空間,能作的是在工程初始的時候,不使用業務名給主 target
命名。
例如咱們要寫一個計算器的應用,那麼很天然的咱們在新建工程名的時候,會給他命名爲 Calculator
。後續若是想要修改工程名,確定也會修改這個 target
。
事實上咱們能夠換一種作法,將主target
命名爲 Main
,而在使用 pod
的時候,指定他生成的 workspace
名. 這樣,後續若是要修改工程名,也只須要修改 workspace
的名字就能達到目的而不去影響 命名空間
platform :ios, '8.0'
# inhibit_all_warnings!
use_frameworks!
workspace 'Calculator'
target 'Main' do
# pod ...
end
複製代碼
若是你以前已經發布了一個 沒有使用過 @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
處理解檔失敗時候的崩潰。