2016-09-29 4 views
2

제네릭 형식을 반환하는 정적 메서드가있는 프로토콜을 만들려고합니다. 대부분의 경우 내가 가진 것이 합리적으로 잘 작동하는 것 같습니다. 확장 기능을 사용하여이 일반 값을 반환하려는 경우 문제가 발생합니다. 여기에 내가 가진 것이있다. 이 코드는 놀이터에 놓을 수 있습니다. 여기 일반 형식의 빠른 프로토콜

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

은 예입니다

는 여기에 내가 나 역 직렬화의 작업을 할 인스턴스를 만들 수 있습니다 다른 프로토콜을 만들어 그 다음 associatedtype

protocol AWSerializable { 
    associatedtype T 

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

이 들어 내가 할 첫 번째 프로토콜입니다 이는 AWSerializable 프로토콜을 기쁘게 구현합니다.

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 
    } 

} 

지금까지 너무 좋아. 문제는 AWDeserializer 프로토콜을 구현하기 위해 UserDefaultsextension을 생성하려고 할 때 발생합니다.

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

여기에 문제가있는 것은 입니다.

enter image description here

이 쉽게 제안 된 솔루션을 적용하여 고정, 또는 바람직 return T.deserialize(dictionary: serialized) as? T

에 라인을 변경하지만이 선택 캐스트는 사실을 좋아하지 않는다 : 나는 다음과 같은 오류가 발생합니다 시작해야합니다. 이 캐스트를 필요로하지 않는 프로토콜을 정의 할 수있는 방법이 있습니까?

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

대신 :

답변

1

은 아마이 프로토콜 AWDeserializer 사용하는 것이 훨씬 낫다

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

을 그리고 여기에 코드의 나머지 부분입니다 :

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

이 가능성이 있고 나는 원래 이것을 가지고 있었다. 나는 2 가지 문제에 부딪쳤다. 첫 번째는 클래스의 Extension에서이 프로토콜을 구현할 수 없다는 것입니다. 'init' 요구 사항은 내가 아는 한 이것을 불가능하게 만든다. 다른 문제는 클래스를 확장 할 때'init' 메쏘드가 수퍼 클래스에서 필요한 이니셜 라이저를 가지고 몇 가지 부작용을 가질 수 있다는 것입니다. – jervine10

+1

만약 그렇다면'? 'AWSerializable.T'와'AWSerializable'은 다를 수 있습니다. Type, 그냥 AWSerializable.T'을 AWSerializable'으로 변환하면됩니다. – beeth0ven

+0

이 프로토콜을 선언하는 유일한 방법입니까? 캐스트가 필요하지 않도록 AWDeserializer를 선언하는 더 좋은 방법이 있습니까? – jervine10

관련 문제