2017-08-18 1 views
3

これは私の一般的なクラスです:汎用カスタムオブジェクトを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") 

私の場合、私はNSKeyedArchiver.archivedDataを呼び出すときcurrentStateのタイプがSMState<SomeEnum>.

である。しかし、Xcodeの(9ベータ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を呼び出すときに、私は、実行時に同じエラーを取得:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: : unrecognized selector sent to instance 
+0

構造体をhttps://developer.apple.com/documentation/foundation/nscodingに準拠させます。 –

+0

'T'を' NSCoding'に制限しようとしますか? – Sweeper

+0

@Sweeper:Tは列挙型です。「非クラス型」のため、NSCodingに制約できません – Kobe

答えて

4

あなたはジェネリッククラスがNSCodingを採用したいとジェネリック型Tは、エンコードとデコードされようとしている場合は、Tは、プロパティリスト対応タイプのいずれかでなければなりません。

プロパティリスト準拠タイプの可能な解決策は、プロトコルPropertyListableを作成し、そのプロトコルに

をプロパティリスト準拠タイプの全てスウィフト均等物を拡張することであるNSStringNSNumberNSDateNSData


ありますプロトコル要件は、

  • A n associated type
  • 計算されたプロパティpropertyListRepresentationプロパティリストに準拠した型に値を変換します。
  • 初期化子init(propertyListこれとは逆です。ここ

public protocol PropertyListable { 
    associatedtype PropertyListType 
    var propertyListRepresentation : PropertyListType { get } 
    init(propertyList : PropertyListType) 
} 

StringIntための例示的な実装です。

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) } 
} 

最後にインスタンスを作成し、それをアーカイブすることができます。この実装では

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") 
    } 
} 

であなたのジェネリッククラスを置き換える

サンプルの列挙型を宣言し、 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)! 
    } 
} 

を採用します

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のようなカスタムタイプが必要ない場合は、実装がはるかに簡単です(短くなります)。

+0

propertyListRepresentationは何をすべきですか? – Kobe

+0

上記のすべてのクラスでは、次のエラーが発生しました。あなたのクラスの重要な部分は 'NSCoder'です。generic型は' NSCoder'でサポートされている必要があります。 – vadian

+0

私はまだ取得しますコンパイル時に私の質問で指定された紫色の警告が表示される – Kobe

0
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クラスの任意の種類の列挙型。任意の列挙型は、AnyHashable

あるため、

は、その後、あなたがこの場合、一般的な

enum A_ENUM_KEY { 
    case KEY_1 
    case KEY_2 
} 

let stateEncodeData = NSKeyedArchiver.archivedData(withRootObject: currentState) 
UserDefaults.standard.set(stateEncodeData, forKey: "state") 

せずに何をしたいと、このSMStateクラスを使用することができ、currentStateのはタイプSMStateのものであり、SMState.valueはSomeEnumです

+0

残念ながら私はまだ同じです問題。理由: '認識できないセレクタがインスタンスに送信されました' " – Kobe

0

"NSInvalidArgumentException '、理由::認識できないセレクタがインスタンスに送信されました"に対処するには、アーカイブしようとしているクラスのスーパークラスもNSCoderを拡張していることを確認してください。

関連する問題