#swift #swift-keypath
Вопрос:
Мне нужна простая реализация сериализации, которая хранит значения различных ключевых путей для класса и применяет их. Я знаю, что это можно реализовать, сохранив переменные вручную, но я бы предпочел использовать ключевые пути, так как было бы проще добавить дополнительные пути. См. Пример ниже:
class Model {
var intValue: Int
var boolValue: Bool
}
class ModelState {
static let keyPaths = [ Model.intValue, Model.boolValue ]
let values: [PartialKeyPath<Model>, Any]
init(model: Model) {
var values = [PartialKeyPath<Model>, Any]()
Self.keyPaths.forEach { values[$0] = model[keyPath: $0 }
self.values = values
}
}
extension Model {
var state: ModelState {
get { return ModelState(model: self }
set { newValue.values.forEach { (keyPath, value) in applyValue(keyPath, value) } }
}
private func applyValue<T>(_ keyPath: PartialKeyPath<Model>, value: T) {
if let writeable = keyPath as? ReferenceWriteableKeyPath<Model, T> {
self[keyPath: writeable] = value
}
}
}
Если я добавлю точку останова в applyValue(), путь к ключу будет показан как ожидаемый путь к ссылкам. Аналогично, если я попытаюсь ввести ключевой путь, явно указав тип ожидаемого значения (например ReferenceWriteableKeyPath<Model, Int>
, или ReferenceWriteableKeyPath<Model, Bool>
), типизация работает правильно. Однако написанный код никогда не входит в условие. Кто-нибудь видит проблему с кодом или знает, как заставить это общее уныние работать?
Комментарии:
1. Это очень сильно противоречит идее ключевых путей, вся суть которых заключалась в том, чтобы быть типобезопасными…
2. Я не вижу противоречия между тем, что я пытаюсь здесь сделать с целью обеспечения безопасности типов. Я хочу безопасно понизить ключевой путь до соответствующего типа значения, чтобы применить его, но я заинтересован в этом только для подмножества свойств модели. У вас есть какие-нибудь советы о том, как это может сработать?
3. Компилятор не может гарантировать типобезопасность вашего кода. В этом смысле он не является типобезопасным. В любом случае, непосредственная проблема заключается в том , что
T
это всегдаAny
так , поэтому вы пытаетесь привести кReferenceWritableKeyPath<Model, Any>
тому, что это не тот же тип,ReferenceWritableKeyPath<Model, Int>
что и . Однако более фундаментальная проблема заключается в том, что вы используете что-то, что обеспечивает безопасность типов (доступные для записи пути ключей) небезопасным способом.4. Хорошо, спасибо, это гораздо полезнее. Я отлаживал и печатал типы этих переменных с помощью type(of:), но вы правы в том, что универсальные типы являются функцией времени компиляции, поэтому при типизации всегда используется T == Any. У вас есть предложение о хорошем способе хранения и применения подмножества свойств объекта? Или в конечном итоге для этого потребуется просто создать структуру для хранения соответствующих свойств и присвоить структуре новое значение?