Код ниже работает нормально:
protocol VariableType {
associatedtype T
var value : T { get }
}
class UserDefaultsVariable<T> : VariableType {
let storageKey : String
var value : T {
get {
return UserDefaults.standard.object(forKey: storageKey) as! T
}
set(value) {
UserDefaults.standard.set(value, forKey: storageKey)
}
}
init( storageKey : String, initialValue : T ) {
self.storageKey = storageKey
// I want dynamic dispatch here so that if T: RawRepresentable then the
// function in the extension is called
self.registerDefaultValue(initialValue: initialValue)
}
func registerDefaultValue( initialValue : T ) {
debugPrint("this is called both times!")
UserDefaults.standard.register(defaults: [storageKey : initialValue])
}
}
Вызов:
let simpleDefault = UserDefaultsVariable<Int>(storageKey: "simple", initialValue: 0)
let initialValue = simpleDefault.value
приводит к тому, что начальное значение равно 0.
Проблема возникает, когда Я пытаюсь расширить это для поддержки RawRepresentable
типов:
extension UserDefaultsVariable where T: RawRepresentable {
var value: T {
get {
let rawValue = UserDefaults.standard.object(forKey: storageKey) as! T.RawValue
return T(rawValue: rawValue)!
}
set {
UserDefaults.standard.set(newValue.rawValue, forKey: storageKey)
}
}
func registerDefaultValue( initialValue : T ) {
debugPrint("this is never called!")
UserDefaults.standard.register(defaults: [storageKey:initialValue.rawValue])
}
}
Когда я вызываю это:
let simpleDefault = UserDefaultsVariable<Int>(storageKey: "simple", initialValue: 0)
let initialValue = simpleDefault.value
enum Direction : Int {
case left = 0
case right = 1
}
let enumedDefault = UserDefaultsVariable<Direction>(storageKey: "enumed", initialValue: .left)
код вылетает, потому что вместо этого вызывается реализация registerDefaults в UserDefaultsVariable<T>
специальной реализации UserDefaultsVariable<T:RawRepresentable>
.
Выполнение вызова из вне инициализатора, например,
enumedDefault.registerDefaultValue(initialValue: .left)
вызывает правильную реализацию !? Итак, похоже, что отправка обычно динамический c, но не внутри инициализатора? или, возможно, весь класс?
Любая помощь будет очень принята.
Решение
Как указывает @matt , Я ошибочно ожидаю, что Swift вызовет ограниченную версию функции с помощью некоторой формы полиморфизма, но компилятор разрешает вызов во время компиляции и не пытается найти «наиболее конкретную c реализацию» ..
@ Asperi представляет собой работоспособное решение, но с тем недостатком, что переменная DefaultsVariable может быть инициализирована без начального значения, чего я пытался избежать.
В конечном итоге я ввел полиморфизм, просто создав подклассы UserDefaultsVariable:
class RawRepresentableUserDefaultsVariable<T: RawRepresentable> : UserDefaultsVariable<T>{
override var value: T {
get {
let rawValue = UserDefaults.standard.object(forKey: storageKey) as! T.RawValue
return T(rawValue: rawValue)!
}
set {
UserDefaults.standard.set(newValue.rawValue, forKey: storageKey)
}
}
override func registerDefaultValue( initialValue : T ) {
UserDefaults.standard.register(defaults: [storageKey:initialValue.rawValue])
}
}
let enumedDefault = RawRepresentableUserDefaultsVariable<Direction>(storageKey: "enumed", initialValue: .left)
let initialEnumValue = enumedDefault.value
Это прекрасно компилируется и вызывает registerDefaultValue
в подклассе из инициализатора в суперклассе.
Я очень благодарен за помощь.