2016-09-29 2 views
2

Ich versuche, ein Protokoll zu erstellen, das über eine statische Methode verfügt, die einen generischen Typ zurückgibt. In den meisten Fällen scheint das, was ich habe, einigermaßen gut zu funktionieren. Die Herausforderung kommt, wenn ich eine Erweiterung verwenden möchte, um diesen generischen Wert zurückzugeben. Hier ist was ich habe. Dieser Code kann auf einem Spielplatz platziert werden.Swift-Protokoll mit generischen Typen

Hier ist das erste Protokoll Ich möchte, die die associatedtype

protocol AWSerializable { 
    associatedtype T 

    static func deserialize(dictionary: [String : Any]) -> T? 
    func serialize() -> [String : Any] 
} 

enthält Dann habe ich ein anderes Protokoll erstellt, das mich Instanzen erstellen können, die die Wirkung von Deserialisieren tun:

protocol AWDeserializer { 
    func deserializeWithKey<T: AWSerializable>(_ key: String!) -> T? 
    func deserializeWithKey<T: AWSerializable>(_ key: String!) -> [T]? 
} 

Hier ist ein Beispiel das implementiert das AWSerializable Protokoll glücklich:

class FooBar: AWSerializable { 

    typealias T = FooBar 

    var foo = "" 
    var bar = "" 

    static func deserialize(dictionary: [String : Any]) -> FooBar? { 
     let fooBar = FooBar() 

     fooBar.foo = (dictionary["foo"] as? String) ?? "" 
     fooBar.bar = (dictionary["bar"] as? String) ?? "" 

     return fooBar 
    } 

    func serialize() -> [String : Any] { 
     var serialized = [String : Any]() 

     serialized["foo"] = foo 
     serialized["bar"] = bar 

     return serialized 
    } 

} 

So weit so gut. Die Herausforderung kommt, wenn ich ein auf UserDefaults erstellen möchte, um das Protokoll AWDeserializer zu implementieren.

extension UserDefaults: AWDeserializer { 
    func deserializeWithKey<T: AWSerializable>(_ key: String!) -> T? { 
     if let serialized = UserDefaults.standard.object(forKey: key) as? [String : Any] { 
      return T.deserialize(dictionary: serialized) 
     } 

     return nil 
    } 

    func deserializeWithKey<T: AWSerializable>(_ key: String!) -> [T]? { 
     if let data = UserDefaults.standard.array(forKey: key) as? [[String : Any]] { 
      var values = [T]() 

      for entry in data { 
       if let value = T.deserialize(dictionary: entry) { 
        values.append(value) 
       } 
      } 

      return values 
     } 

     return nil 
    } 
} 

Das Problem hier ist mit T.deserialize(dictionary: serialized). Ich erhalte die folgende Fehlermeldung:

enter image description here

Dies wird durch die Anwendung der vorgeschlagenen Lösung leicht fixiert ist, oder vorzugsweise durch die Leitung zu return T.deserialize(dictionary: serialized) as? T Ändern

Aber ich weiß nicht, wie die Tatsache, dass diese optionale Besetzung ist erforderlich, um damit zu beginnen. Gibt es eine Möglichkeit, die Protokolle zu definieren, für die diese Besetzung nicht erforderlich ist?

Antwort

1

Vielleicht ist es viel besser, dieses Protokoll AWDeserializer zu verwenden:

protocol AWDeserializer { 
    func deserializeWithKey<T: AWSerializable>(_ key: String!) -> T? where T.T == T 
    func deserializeWithKey<T: AWSerializable>(_ key: String!) -> [T]? where T.T == T 
} 

Statt:

protocol AWDeserializer { 
    func deserializeWithKey<T: AWSerializable>(_ key: String!) -> T? 
    func deserializeWithKey<T: AWSerializable>(_ key: String!) -> [T]? 
} 

Und hier ist der Rest des Codes:

protocol AWSerializable { 
    associatedtype T 

    static func deserialize(dictionary: [String : Any]) -> T? 
    func serialize() -> [String : Any] 
} 

protocol AWDeserializer { 
    func deserializeWithKey<T: AWSerializable>(_ key: String!) -> T? where T.T == T 
    func deserializeWithKey<T: AWSerializable>(_ key: String!) -> [T]? where T.T == T 
} 

class FooBar: AWSerializable { 

    typealias T = FooBar 

    var foo = "" 
    var bar = "" 

    static func deserialize(dictionary: [String : Any]) -> FooBar? { 
     let fooBar = FooBar() 

     fooBar.foo = (dictionary["foo"] as? String) ?? "" 
     fooBar.bar = (dictionary["bar"] as? String) ?? "" 

     return fooBar 
    } 

    func serialize() -> [String : Any] { 
     var serialized = [String : Any]() 

     serialized["foo"] = foo 
     serialized["bar"] = bar 

     return serialized 
    } 

} 

extension UserDefaults: AWDeserializer { 
    func deserializeWithKey<T: AWSerializable>(_ key: String!) -> T? where T.T == T { 
     if let serialized = UserDefaults.standard.object(forKey: key) as? [String : Any] { 
      return T.deserialize(dictionary: serialized) 
     } 

     return nil 
    } 

    func deserializeWithKey<T: AWSerializable>(_ key: String!) -> [T]? where T.T == T { 
     if let data = UserDefaults.standard.array(forKey: key) as? [[String : Any]] { 
      var values = [T]() 

      for entry in data { 
       if let value = T.deserialize(dictionary: entry) { 
        values.append(value) 
       } 
      } 

      return values 
     } 

     return nil 
    } 
} 
+0

Dies ist eine Möglichkeit und Ich hatte das ursprünglich. Ich habe 2 Probleme bekommen. Der erste ist, dass ich dieses Protokoll nicht auf einer Erweiterung einer Klasse implementieren kann. Die "init" -Anforderung macht dies soweit ich weiß nicht möglich. Das andere Problem besteht darin, dass beim Erweitern einer Klasse die 'init'-Methode einige Nebenwirkungen mit erforderlichen Initialisierern in der Superklasse haben kann. – jervine10

+1

Wenn ja, so? T' ist erforderlich, da 'AWSerializable.T' und' AWSerializable' unterschiedliche Typen sein können. Verwenden Sie diese Option, um 'AWSerializable.T' auf' AWSerializable' zu ​​setzen. – beeth0ven

+0

Ist das die einzige Möglichkeit, diese Protokolle zu deklarieren? Gibt es eine bessere Möglichkeit, 'AWDeserializer' zu deklarieren, so dass Cast nicht benötigt wird? – jervine10

Verwandte Themen