我正在尝试为我的应用创建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)
}
}
答案 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的所有方法中都是一致的。这是一种相关的类型。