2016-10-24 3 views
0

Я пытаюсь создать хранилище generic для своего приложения, где могут храниться Serializable элементов.Общий параметр T не может быть выведен

У меня есть несколько структур, которые реализуют 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) 
     } 
    } 
+0

Как вы определили 'self.storage'. Из-за ошибки я думаю, что это не в формате, где объекты проверяют 'Serializable'. –

+4

Попробуйте добавить информацию о типе элементам, например:' self.storage.getIems {(elements: [CarouselPoi]) в' – Connor

+0

не работает .. 'Невозможно преобразовать значение типа' ([CarouselPoi]) ->() 'до ожидаемого типа аргумента' ([_]?) -> Void' – croigsalvador

ответ

3

Этот протокол не делать то, что вы думаете, что делает:

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

Это говорит о том, что Сериализуемый можно сериализовать в словарь, и что любой тип Сериализуемый имеет статический метод, который будет конвертировать словарь в что-то (T). У этого «чего-то» нет никаких обещаний. Это не имеет никакого отношения к типу Serializable. Компилятор не имеет возможности угадать этот тип, кроме как путем просмотра того, что вы запросили для возвращаемого значения.

Что вы имели в виду почти наверняка является то, что Сериализуемый можно десериализации из словаря:

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

Это говорит о том, что вы имеете в виду, но это почти невозможно осуществить таким образом, чтобы не врезаться. Что делать, если словарь не содержит ключей, которые вы ожидаете? Что ты тогда возвращаешь? Этот метод должен быть либо факультативным, либо метательным, и он будет намного более быстрым как init. Например:

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. Вот как работают дженерики. То, что вы хотите, это тип, который is tied к Storable и согласован во всех методах в Storable. Это связанный тип.

+0

I попробовали ваш подход, но я получаю те же проблемы .. Я отредактировал свой вопрос – croigsalvador

+0

У вас такая же проблема при десериализации и в getItems. Вы продолжаете вводить параметр T, который вы не даете компилятору каким-либо образом ограничить. Что вы ожидаете от того, что T окажется в строке, на которой вы получаете ошибку? –

+0

В StorageCoordinators я все еще хочу элементы T, Это в классе, где я называю StorageCoordinators, где мне нужен реальный объект. Извините, но я не очень хорошо разбираюсь в дженериках. Я попытался добавить 'as' в guard, где я получаю элементы после вызова getItems, но это не работает – croigsalvador

Смежные вопросы