首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >在RawRepresentable上实现UnitMass

在RawRepresentable上实现UnitMass
EN

Stack Overflow用户
提问于 2021-04-24 01:26:03
回答 2查看 154关注 0票数 1

我试图在Measurement<UnitMass>UnitMass上实现@AppStorage,以便用@AppStorage装饰器替换以下代码:

代码语言:javascript
复制
var unitOfMeasure: UnitMass {
    get { AppSettings.defaults.string(forKey: "unitOfMeasure").flatMap { UnitMass.fromSymbol(rawValue: $0) }! }
    set { AppSettings.defaults.set(newValue.symbol, forKey: "unitOfMeasure") }
}

var weightOverwrite: Measurement<UnitMass> {
    get { .init(value: AppSettings.defaults.double(forKey: "weightOverwrite"), unit: unitOfMeasure) }
    set { AppSettings.defaults.set(newValue.value, forKey: "weightOverwrite") }
}

我该怎么做?我使用JSONEncoder/JSONDecoder实现了它:

代码语言:javascript
复制
extension Measurement: RawRepresentable {
    public init?(rawValue: String) {
        guard let data = rawValue.data(using: .utf8),
            let result = try? JSONDecoder().decode(Measurement.self, from: data)
        else {
            return nil
        }
        self = result
    }

    public var rawValue: String {
        guard let data = try? JSONEncoder().encode(self),
            let result = String(data: data, encoding: .utf8)
        else {
            return "{}"
        }
        return result
    }
}

但我不能为UnitMass做这件事:

代码语言:javascript
复制
extension UnitMass: RawRepresentable {
    public init?(rawValue: String) {
        for unitMass in UnitMass.allCases where rawValue == unitLength.symbol {
            self = unitLength
        }
        
        return nil
    }
}

我得到了Designated initializer cannot be declared in an extension of 'UnitMass'。我做错了什么?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2021-04-24 03:13:00

这就是为什么您不能使UnitMassRawRepresentable相一致的原因。

RawRepresentable有这样的要求:一致性类必须有一个init(rawValue:)初始化器。

UnitMass不是final,所以它可以有子类。

如果UnitMassRawRepresentable一致,则UnitMass的子类也符合RawRepresentable,因此它们也必须具有init(rawValue:)

如何实现子类中的init(rawValue:)?请注意,它们不能只继承UnitMass中的实现,因为子类可能有自己存储的属性,需要在初始化器中初始化。

因此,您的扩展需要UnitMass的所有子类来实现这个新的初始化器。好吧,扩展不应该添加需求-它们应该添加功能!

即使扩展可以做到这一点,对于您来说,访问UnitMass的每个子类并添加init(rawValue:):的实现也是不切实际的。)

总之,这里有一些解决办法:

使用包装类:

代码语言:javascript
复制
class MyUnitMass: RawRepresentable {
    let unitMass: UnitMass
    
    var rawValue: String {
        unitMass.symbol
    }
    
    required init?(rawValue: String) {
        // assuming fromSymbol actually uses the correct converter
        unitMass = UnitMass.fromSymbol(rawValue: rawValue)
    }
}

或者,将UnitMassUserDefaults中保存为Data而不是String,因为UnitMass符合NSSecureCoding

代码语言:javascript
复制
let data = try NSKeyedArchiver.archivedData(withRootObject: UnitMass.grams, requiringSecureCoding: false)

// save "data" to UserDefaults instead

let unitMass = try NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data) as! UnitMass

这也具有编码converter的优点,而不必在fromSymbol中对其进行硬编码(想必您现在正在做的事情)。

还请注意,如果只将unitOfMeasure设置用作weightOverwrite的单元,则应该将weightOverwrite保存在用户默认值中,并按以下方式声明unitOfMeasure

代码语言:javascript
复制
@AppStorage("hello", store: UserDefaults.standard)
var weightOverwrite: Measurement<UnitMass> = Measurement(value: 1, unit: .grams)

var unitOfMeasure: UnitMass {
    get { weightOverwrite.unit }
    set { weightOverwrite.convert(to: newValue) }
}
票数 4
EN

Stack Overflow用户

发布于 2021-10-31 08:20:50

您可以扩展RawReprresentable本身,以便UnitMass自动开始执行它。

代码语言:javascript
复制
extension RawRepresentable where Self: NSSecureCoding {
var rawValue: String {
    let data = try? NSKeyedArchiver.archivedData(withRootObject: self, requiringSecureCoding: false)
    return data!.base64EncodedString()
}

init?(rawValue: String) {
    if let data = Data(base64Encoded: rawValue), let unit = (try? NSKeyedUnarchiver.unarchiveTopLevelObjectWithData(data)) as? Self {
            self = unit
        } else {
            return nil
        }
    }
}

extension UnitMass: RawRepresentable { }
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67238540

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档