【问题标题】:Most specific generic function not called最具体的通用函数未调用
【发布时间】:2020-01-20 09:28:11
【问题描述】:

我正在使用@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

我错过了什么?关于如何解决这个问题的任何想法?

【问题讨论】:

    标签: swift generics swift5


    【解决方案1】:

    我错过了什么?

    Swift 本质上是一种静态类型语言,选择要调用的函数重载是在编译时确定的。

    在您的工作示例中:

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

    编译器知道第一个参数的类型。这种类型实现了RawRepresentable,编译器使用它来选择你期望的重载。

    现在考虑你失败的例子:

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

    当编译器编译set 函数时,它唯一知道的参数value 就是它有一个可以引用为T 的类型。 T 没有限制,在运行时可以传递 any 类型的值,因此在确定 UserDefaults.set 的哪个重载来编译对编译器的调用时,只能选择也没有约束并接受任何类型的值。

    有什么想法可以解决这个问题吗?

    您已经知道一种解决方案,您重载了UserDefaults.set,您可以重载您的set 函数。但是,您可能希望根据 Swift 对重载的编译时解析来考虑您的设计——您可能不希望重载函数层相互调用。

    HTH

    【讨论】:

    • 感谢您抽出宝贵时间给予如此详细的答复。
    猜你喜欢
    • 2018-03-14
    • 1970-01-01
    • 2013-12-07
    • 2014-07-30
    • 2012-09-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-03
    • 2015-10-15
    相关资源
    最近更新 更多