[英]Trying to save custom object in UserDefaults using NSKeyedArchiver
[英]How to save a generic custom object to UserDefaults?
這是我的通用類:
open class SMState<T: Hashable>: NSObject, NSCoding {
open var value: T
open var didEnter: ( (_ state: SMState<T>) -> Void)?
open var didExit: ( (_ state: SMState<T>) -> Void)?
public init(_ value: T) {
self.value = value
}
convenience required public init(coder decoder: NSCoder) {
let value = decoder.decodeObject(forKey: "value") as! T
self.init(value)
}
public func encode(with aCoder: NSCoder) {
aCoder.encode(value, forKey: "value")
}
}
然后我想這樣做:
let stateEncodeData = NSKeyedArchiver.archivedData(withRootObject: currentState)
UserDefaults.standard.set(stateEncodeData, forKey: "state")
在我的例子中, currentState
的類型為SMState<SomeEnum>.
但是,當我調用NSKeyedArchiver.archivedData
,Xcode(9 beta 5)顯示紫色消息說:
Attempting to archive generic Swift class 'StepUp.SMState<StepUp.RoutineViewController.RoutineState>' with mangled runtime name '_TtGC6StepUp7SMStateOCS_21RoutineViewController12RoutineState_'. Runtime names for generic classes are unstable and may change in the future, leading to non-decodable data.
我不確定它想說什么。 是不是可以保存通用對象?
有沒有其他方法來保存通用自定義對象?
edit
:
即使我使用AnyHashable
而不是泛型,我在調用NSKeyedArchiver.archivedData
時AnyHashable
在運行時遇到相同的錯誤:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: : unrecognized selector sent to instance
如果要使泛型類采用NSCoding
並且將對泛型類型T
進行編碼和解碼,則T
必須是屬性列表兼容類型之一 。
符合屬性列表的類型是NSString
, NSNumber
, NSDate
和NSData
一種可能的解決方案是創建一個協議PropertyListable
,並將屬性列表兼容類型的所有Swift等價物擴展到該協議
協議要求是
associated type
。 propertyListRepresentation
用於將值轉換為屬性列表兼容類型。 init(propertyList
做相反的操作)。 public protocol PropertyListable {
associatedtype PropertyListType
var propertyListRepresentation : PropertyListType { get }
init(propertyList : PropertyListType)
}
這是String
和Int
示例性實現。
extension String : PropertyListable {
public typealias PropertyListType = String
public var propertyListRepresentation : PropertyListType { return self }
public init(propertyList: PropertyListType) { self.init(stringLiteral: propertyList) }
}
extension Int : PropertyListable {
public typealias PropertyListType = Int
public var propertyListRepresentation : PropertyListType { return self }
public init(propertyList: PropertyListType) { self.init(propertyList) }
}
讓我們聲明一個示例枚舉並采用PropertyListable
enum Foo : Int, PropertyListable {
public typealias PropertyListType = Int
case north, east, south, west
public var propertyListRepresentation : PropertyListType { return self.rawValue }
public init(propertyList: PropertyListType) {
self.init(rawValue: propertyList)!
}
}
最后用。替換你的泛型類
open class SMState<T: PropertyListable>: NSObject, NSCoding {
open var value: T
open var didEnter: ( (_ state: SMState<T>) -> Void)?
open var didExit: ( (_ state: SMState<T>) -> Void)?
public init(_ value: T) {
self.value = value
}
convenience required public init(coder decoder: NSCoder) {
let value = decoder.decodeObject(forKey: "value") as! T.PropertyListType
self.init(T(propertyList: value))
}
public func encode(with aCoder: NSCoder) {
aCoder.encode(value.propertyListRepresentation, forKey: "value")
}
}
通過此實現,您可以創建實例並將其存檔
let currentState = SMState<Foo>(Foo.north)
let stateEncodeData = NSKeyedArchiver.archivedData(withRootObject: currentState)
並再次取消歸檔
let restoredState = NSKeyedUnarchiver.unarchiveObject(with: stateEncodeData) as! SMState<Foo>
print(restoredState.value)
整個解決方案似乎很麻煩,但您必須滿足NSCoding
要求屬性列表兼容類型的限制。 如果您不需要像enum
那樣的自定義類型,則實現更容易(也更短)。
open class SMState: NSObject, NSCoding {
open var value: AnyHashable
open var didEnter: ( (_ state: SMState) -> Void)?
open var didExit: ( (_ state: SMState) -> Void)?
public init(_ value: AnyHashable) {
self.value = value
}
convenience required public init(coder decoder: NSCoder) {
let value = decoder.decodeObject(forKey: "value") as! AnyHashable
self.init(value)
}
public func encode(with aCoder: NSCoder) {
aCoder.encode(value, forKey: "value")
}
}
現在這個SMState類就像是SMState<T: Hashable>
,你可以在這個SMState類中發送任何類型的枚舉類型。
然后你可以使用這個SMState類,而不需要Generic
enum A_ENUM_KEY {
case KEY_1
case KEY_2
}
let stateEncodeData = NSKeyedArchiver.archivedData(withRootObject: currentState)
UserDefaults.standard.set(stateEncodeData, forKey: "state")
在這種情況下,currentState的類型為SMState,而SMState.value是SomeEnum,因為Any Enums是AnyHashable
要解決“NSInvalidArgumentException”,原因::發送到實例的無法識別的選擇器“,請確保您嘗試存檔的類的超類也擴展了NSCoder。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.