无法推断出通用参数T.

时间:2016-10-24 14:45:46

标签: swift generics swift3

我正在尝试为我的应用创建generic存储空间,其中可以存储Serializable个项目。

我有一些实现Serializable protocol

的结构
 protocol Serializable {
    func serialize() -> [String: AnyObject]
    init?(byDeserializing dictionary : [String: AnyObject])
}

这是我的Storage Protocol

protocol Storage {
    func getItems<T:Serializable>(completion : @escaping ([T]?)-> Void )
    func save<T:Serializable>(_ items : [T], completion : @escaping (Bool)-> Void )
}

extension Storage  {

    func data<T:Serializable>(from serializableItems : [T]) -> Data? {

        var serializedItems = [Dictionary<String,AnyObject>]()

        for item in serializableItems {
            serializedItems.append(item.serialize())
        }

        guard let serializedData = try? PropertyListSerialization.data(fromPropertyList: serializedItems, format:.binary, options:0) else {
            return nil;
        }
        return serializedData

    }

     func serializedItems(from data : Data) -> [Dictionary<String, AnyObject>]? {

        guard  let serilizedItems = try? PropertyListSerialization.propertyList(from: data, options: .mutableContainers, format: nil) as? [Dictionary<String,AnyObject>] else {
            return nil
        }
        return serilizedItems
    }

    func deserialize<T:Serializable>(from serializedItems: [[String : AnyObject]]  ) -> [T] {
        var items = [T]()
        for serializedItem in serializedItems {
            if let item = T(byDeserializing:serializedItem){
                items.append(item)
            }
        }
        return items
    }
}

当应用想要恢复存储的商品时,只需要调用self.storage.getItems ....

func getItems<T : Serializable>(completion: @escaping ([T]?) -> Void) {
        let path = fileURL().path

        concurrentQueue.async {
            guard let serializedItems = NSArray(contentsOfFile: path) as? [[String : AnyObject]], serializedItems.count > 0 else {
                completion(nil)
                return
            }

            let deserializedItems = self.deserialize(from: serializedItems)
            completion(deserializedItems)
        }
    }

我调用了getItems方法,我在两个存储协调器中都得到了这个编译错误

PlistStorageCoordinator

Generic Error

UserDefaultrsStorageCoordinator

Generic inferred error 它完美地工作,直到我将泛型添加到此方法。有谁知道什么是错的?

我不知道为什么会这样修复它。我不喜欢它,因为我在两个存储中复制代码。有人可以向我解释一下吗?

 func getItems<T : Serializable>(completion: @escaping ([T]?) -> Void) {

        concurrentQueue.async {
            guard  let data = self.userDefaults.data(forKey: self.modelKey), let serializedItems = self.serializedItems(from: data), serializedItems.count > 0 else {
                completion(nil)
                return
            }
            var items = [T]()
            for serializedItem in serializedItems {
                if let item = T(byDeserializing:serializedItem){
                    items.append(item)
                }
            }
            completion(items)
        }
    }

1 个答案:

答案 0 :(得分:3)

此协议不符合您的预期:

protocol Serializable {
    func serialize() -> Dictionary<String, AnyObject>
    static func deserialize<T>(_ dictionary : Dictionary<String,AnyObject>) -> T
}

这表示Serializable可以序列化为字典,并且任何Serializable类型都有一个静态方法,可以将字典转换为某种东西(T)。那个“东西”没有任何承诺。它与Serializable类型无关。除了查看您请求返回值的内容之外,编译器无法猜测此类型。

你的意思几乎可以肯定是Serializable可以从字典中反序列化:

protocol Serializable {
    func serialize() -> Dictionary<String, AnyObject>
    static func deserialize(_ dictionary : [String: AnyObject]) -> Self
}

这说明了你的意思,但几乎不可能以不会崩溃的方式实施。如果字典不包含您期望的键,该怎么办?那你回来了什么?这个方法应该是可选的或者抛出,并且作为init会有很多Swift。例如:

protocol Serializable {
    func serialize() -> [String: AnyObject]
    init?(byDeserializing dictionary: [String: AnyObject])
}

有了这个,你的系统就会有更多的功能。

(所有这一切,请务必查看NSCoding,它已经以更强大的方式完成了您正在尝试做的事情。有理由不使用NSCoding,但要确保这是一个积极的选择,而不仅仅是重新发明它。)

这个协议也没有说出你的意思:

protocol Storage {
    func getItems<T:Serializable>(completion : @escaping ([T]?)-> Void )
    func save<T:Serializable>(_ items : [T], completion : @escaping (Bool)-> Void )
}

这表示Storage可以返回任何可序列化类型的项目列表,并且可以保存任何可序列化类型的项目列表。这些类型不必以任何方式相关。您的意思是存储可以获取和保存与该存储相关联的特定类型的项目。在这种情况下,您需要一个关联的类型:

protocol Storage {
    associatedType Element
    func getItems(completion : @escaping ([Element]?)-> Void )
    func save(_ items : [Element], completion : @escaping (Bool)-> Void )
}

此功能:

func getItems<T:Serializable>(completion : @escaping ([T]?)-> Void )

有两个参数。您可能理解的第二个参数。它是completion,它是一个带有可选数组并返回Void的函数。但我相信你误解了第一个参数:T。当您致电getItems时,您隐式传递类型作为其中一个参数。每次拨打getItems时,您都可以传递不同的T(就像您可以传递不同的completion一样。T没有任何内容可以将其与此可存储相关联。这就是泛型的工作方式。你想要的是 与Storable相关的类型,并且在Storable的所有方法中都是一致的。这是一种相关的类型。