测试是否符合 RawRepresentable 协议并转换为 RawRepresentable 协议

Testing for compliance with and casting to RawRepresentable protocol

我有一些通用代码,可以让我读取各种类型并将其写入默认系统,例如值获取器和设置器:

var value : T {
    
    get {
        if T.self == Int.self {
            return UserDefaults.standard.integer(forKey: storageKey) as! T
        } else if T.self == Double.self {
            return UserDefaults.standard.double(forKey: storageKey) as! T
        } else if T.self == Float.self {
            return UserDefaults.standard.float(forKey: storageKey) as! T
        } else if T.self == Bool.self {
            return UserDefaults.standard.bool(forKey: storageKey) as! T
        } else if T.self == String.self {
            return UserDefaults.standard.string(forKey: storageKey) as! T
        } else {
            return UserDefaults.standard.value(forKey: self.storageKey) as! T
        }
    }
    
    set(value) {
        UserDefaults.standard.set(value, forKey: storageKey)
        UserDefaults.standard.synchronize()
    }
}

现在我想将我自己的枚举类型添加到此机制中,方法是将它们设为 RawRepresentable<Int>,例如

enum Direction : Int, RawRepresentable {
    case left = 0
    case right = 1
}

不幸的是,我既找不到测试T是否符合RawRepresentable协议的魔术,也无法将T转换为RawRepresentable协议,因为无论我尝试什么,我总是以 Protocol 'RawRepresentable' can only be used as a generic constraint because it has Self or associated type requirements.

结束

我已经尝试了每一个 whereas 咒语,直到我开始怀疑它是否可以完成!?

我在 Swift 5,目标是通过调用 CustomType(rawValue:) 创建新实例并通过调用 myValue.rawValue.

获取 Int 值

正如@vadian 所说,所有这些类型检查都可以替换为对 UserDefaults.standard.object() 的单个调用和条件转换。 value 属性 的类型也需要是可选的,以处理 属性 未设置(或类型不正确)的情况:

struct DefaultKey<T> {
    let storageKey: String
    
    var value: T? {
        get {
            return UserDefaults.standard.object(forKey: storageKey) as? T
        }
        nonmutating set {
            UserDefaults.standard.set(newValue, forKey: storageKey)
        }
    }
}

然后你可以定义一个约束扩展方法,你可以在其中专门针对 RawRepresentable 类型的情况计算 属性:

extension DefaultKey where T: RawRepresentable {
    var value: T? {
        get {
            if let rawValue = UserDefaults.standard.object(forKey: storageKey) as? T.RawValue {
                return T(rawValue: rawValue)
            }
            return nil
        }
        nonmutating set {
            UserDefaults.standard.set(newValue?.rawValue, forKey: storageKey)
        }
    }
}

用法示例:

enum Direction : Int {
    case left = 0
    case right = 1
}

let key1 = DefaultKey<Int>(storageKey: "foo")
key1.value = 123
let key2 = DefaultKey<Direction>(storageKey: "bar")
key2.value = .right

print(key1.value as Any) // Optional(123)
print(key2.value as Any) // Optional(Direction.right)

请注意,如果与非 属性 列表类型一起使用,这仍然会崩溃。为了安全起见,您必须将扩展限制为已知用户默认可存储的类型(整数、浮点数、字符串...):

protocol UserDefaultsStorable {}
extension Int: UserDefaultsStorable {}
extension Float: UserDefaultsStorable {}
// ...

struct DefaultKey<T> {
        let storageKey: String
}

extension DefaultKey where T: UserDefaultsStorable { .. }

extension DefaultKey where T: RawRepresentable, T.RawValue: UserDefaultsStorable { ... }