【问题标题】:Generic parameter T could not be inferred无法推断通用参数 T
【发布时间】:2016-10-24 14:45:46
【问题描述】:

我正在尝试为我的应用创建一个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

UserDefaultrsStorageCoordinator

在我向这个方法添加泛型之前,它工作得很好。有谁知道哪里出了问题?

我不知道为什么,但这可以解决它。我不喜欢它,因为我在两个存储中都复制了代码。谁能给我解释一下?

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

【问题讨论】:

  • 你是如何定义self.storage的。从错误中我认为它不是以对象确认Serializable 协议的格式。
  • 尝试将类型信息添加到元素中,例如:self.storage.getIems { (elements:[CarouselPoi]) in
  • 它不起作用.. Cannot convert value of type ([CarouselPoi]) -> ()` 到预期的参数类型([_]?) -&gt; Void

标签: swift generics swift3


【解决方案1】:

这个协议并没有像你想象的那样做:

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

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

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

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

这说明了您的意思,但几乎不可能以不会崩溃的方式实施。如果字典不包含您期望的键怎么办?那你还什么?这个方法应该是可选的或抛出的,它会像 init 一样 Swiftier。例如:

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 可以返回任何可序列化类型的项目列表,并且可以保存任何可序列化类型的项目列表。这些类型不必以任何方式相关。您的意思似乎是 Storage 可以获取和保存与该 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 时,您将隐式传递一个 type 作为参数之一。每次你调用getItems,你可以传递一个不同的T(就像你可以传递一个不同的completionT 没有任何东西与这个 Storable 相关联。这就是泛型的工作原理。你想要什么是一种 绑定到 Storable 的类型,并且在 Storable 中的所有方法中都是一致的。这是一个关联类型。

【讨论】:

  • 我已经尝试过你的方法,但我遇到了同样的问题。我已经编辑了我的问题
  • 您在反序列化和 getItems 中遇到了同样的问题。你不断注入一个 T 参数,你没有给编译器任何约束。您希望 T 出现在您遇到错误的那一行?
  • 在 StorageCoordinators 中我仍然想要 T 元素,它是在我需要真实对象的 StorageCoordinators 类中。对不起,我不太了解泛型。我尝试在 getItems 调用后将 as 添加到我获取项目的守卫中,但它不起作用
猜你喜欢
  • 1970-01-01
  • 2018-05-19
  • 1970-01-01
  • 1970-01-01
  • 2015-09-22
  • 2021-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多