具有泛型类型的Swift协议

时间:2016-09-29 14:03:45

标签: swift generics swift-protocols swift-extensions

我试图创建一个具有返回泛型类型的静态方法的协议。在大多数情况下,我所拥有的似乎运作得相当好。当我想使用扩展来返回这个通用值时,挑战就出现了。这就是我所拥有的。这段代码可以放在游乐场里。

这是我想要的第一个包含associatedtype

的协议
protocol AWSerializable {
    associatedtype T

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

然后我创建了另一个协议,允许我创建执行反序列化操作的实例:

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

以下是一个快乐实现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
    }

}

到目前为止一切顺利。当我想在extension上创建UserDefaults来实施AWDeserializer协议时,就会遇到挑战。

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

这里的问题是T.deserialize(dictionary: serialized)。我收到以下错误:

enter image description here

通过应用建议的解决方案,或者最好将行更改为return T.deserialize(dictionary: serialized) as? T

,可以轻松解决此问题

但我不喜欢这个可选的演员阵容需要开始的事实。有没有办法定义不需要这种演员的协议?

1 个答案:

答案 0 :(得分:1)

使用此协议AWDeserializer可能会好得多:

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

而不是:

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