Самая специфическая общая функция не вызывается - PullRequest
0 голосов
/ 19 сентября 2019

Я использую @propertyWrapper, чтобы уменьшить шаблон UserDefaults следующим образом…

enum PreferenceKey: String, CaseIterable {
    case enumName, stringName
}

@propertyWrapper
struct Prefs<T> {
    let key: PreferenceKey

    var wrappedValue: T? {
        get {
            UserDefaults.object(for: key)
        }
        set {
            UserDefaults.set(newValue, for: key)
        }
    }
}

struct Preferences {
    @Prefs(key: .enumName) static var enumName: Name?
    @Prefs(key: .stringName) static var stringName: String?
}

extension UserDefaults {

    static func object<T>(for key: PreferenceKey) -> T? {
        standard.object(forKey: key.rawValue) as? T
    }

    static func object<T: RawRepresentable>(for key: PreferenceKey) -> T? where T.RawValue == String {
        if let value = standard.object(forKey: key.rawValue) as? String {
            return T(rawValue: value)
        }
        return nil
    }

    static func set<T: RawRepresentable>(_ value: T, for key: PreferenceKey) {
        print("Set Raw Value \(value)")
        standard.set(value.rawValue, forKey: key.rawValue)
    }
    static func set<T>(_ value: T, for key: PreferenceKey) {
        print("Set Value \(value)")
        standard.set(value, forKey: key.rawValue)
    }

}

Это прекрасно работает при установке обычного типа списка свойств…

Preferences.stringName = "Fred"
// Set Value Optional("Fred")

print(Preferences.stringName)
// Optional("Fred")

Нопри попытке установить значение, равное RawRepresentable, происходит сбой…

Preferences.enumName = .Fred

// Set Value Optional(__lldb_expr_10.Name.Fred)
// libc++abi.dylib: terminating with uncaught exception of type NSException

Вместо вызова самой конкретной версии UserDefaults.set(, она вызывает неспецифическую версию.

Просто звонить

UserDefaults.set(Name.Fred, for: .enumName)

работает нормально.В этом случае он вызывает наиболее специфическую функцию.


При дальнейшем тестировании, и кажется, что это не проблема @propertyWrapper.Следующая функция верхнего уровня также не может вызвать более конкретную универсальную функцию.Кажется, что какая-то информация типа теряется где-то

func set<T>(_ value: T?) {
    UserDefaults.set(value, for: .enumName)
}

set(Name.Fred)
// Set Value Optional(__lldb_expr_5.Name.Fred)
// libc++abi.dylib: terminating with uncaught exception of type NSException

Чего мне не хватает?Любые мысли о том, как я могу решить эту проблему?

1 Ответ

2 голосов
/ 22 сентября 2019

Чего мне не хватает?

По сути, Swift является языком со статической типизацией, и выбор перегрузок вашей функции для вызова определяется во время компиляции.

В вашем рабочем примере:

UserDefaults.set(Name.Fred, for: .enumName)

тип первого аргумента известен компилятору.Этот тип реализует RawRepresentable, и компилятор использует его для выбора ожидаемой перегрузки.

Теперь рассмотрим ваш ошибочный пример:

func set<T>(_ value: T?) {
   UserDefaults.set(value, for: .enumName)
}

set(Name.Fred)

Когда компилятор компилируетset function единственное, что он знает об аргументе value, это то, что он имеет тип, на который он может ссылаться как T.Нет никаких ограничений на T, во время выполнения может быть передано значение любого типа , поэтому при определении того, какая перегрузка UserDefaults.set для компиляции вызова компилятору, можно выбрать только перегрузку, которая также имеетнет ограничений и принимает значения любого типа.

Есть мысли о том, как я могу решить эту проблему?

Вы уже знаете одно решение,вы перегружены UserDefaults.set, вы можете перегрузить функцию set.Однако вы можете рассмотреть свой дизайн здесь в свете разрешения перегрузок во время компиляции Swift - вам может не потребоваться, чтобы слои перегруженных функций вызывали друг друга.

HTH

...