未调用最具体的通用函数

Most specific generic function not called

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

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

What am I missing?

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

在您的工作示例中:

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

编译器知道第一个参数的类型。此类型实现 RawRepresentable 并且编译器使用它来 select 您期望的重载。

现在考虑你失败的例子:

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

set(Name.Fred)

当编译器编译 set 函数时 它唯一知道参数 value 的是它有一个可以引用的类型TT没有约束,在运行时可以传递任何类型的值,所以在确定UserDefaults.set的哪个重载编译调用编译器只能 select 也没有约束并接受任何类型的值的重载。

Any thoughts as to how I can resolve this?

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

HTH