首页 > 解决方案 > 测试是否符合 RawRepresentable 协议并强制转换为 RawRepresentable 协议

问题描述

我有一些通用代码允许我在默认系统中读取和写入各种类型,例如值获取器和设置器:

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:)获取 Int 值myValue.rawValue

标签: swiftgenericsprotocols

解决方案


正如@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 { ... }

推荐阅读