swift - 在 UnitMass 上实现 RawRepresentable
问题描述
我正在尝试实现RawRepresentable
并用装饰器Measurement<UnitMass>
替换以下代码:UnitMass
@AppStorage
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 实现了它:
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 做到这一点:
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'
。我究竟做错了什么?
解决方案
这就是为什么你不能UnitMass
遵守RawRepresentable
.
RawRepresentable
要求符合的类必须有一个init(rawValue:)
初始化器。
UnitMass
不是final
,所以它可以有子类。
的子类UnitMass
也符合RawRepresentable
如果UnitMass
符合RawRepresentable
,所以它们也必须有init(rawValue:)
。
如何init(rawValue:)
在子类中实现?请注意,它们不能只继承 中的实现UnitMass
,因为子类可能有自己的存储属性,需要在初始化程序中进行初始化。
所以你的扩展需要所有的子类UnitMass
来实现这个新的初始化器。好吧,扩展不应该添加需求——它们应该添加功能!
即使扩展可以做到这一点,你去每个子类UnitMass
并添加一个实现也是不切实际的init(rawValue:)
:)
无论如何,这里有一些解决方法:
使用包装类:
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)
}
}
或者,将UnitMass
in保存UserDefaults
为Data
而不是String
,因为UnitMass
符合NSSecureCoding
.
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
如下:
@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) }
}
推荐阅读
- java - java.lang.UnsatisfiedLinkError: C:\...\xxx.dll: Can't load this .dll (machine code=0xbd) on a AMD 64-bit platform
- ios - FCM 推送通知在返回应用程序时打开,而不是在 iOS 的状态栏中显示
- python - 如何修复 Django 中的“AttributeError at /api/doc 'AutoSchema' object has no attribute 'get_link'”错误
- python - 从仅在登录后可用的网站获取受限数据
- clojurescript - 在clojurescript /试剂中跟踪鼠标并在屏幕上渲染点?
- web - 如何在我的 Genexus 项目中使用第三方控件
- ruby-on-rails - 如何在 rails admin 中配置侧边菜单
- python - 我缺少什么空格修饰符?
- .net - 带反射的自动映射器映射
- c# - System.InvalidOperationException:找不到包 MessagePack 的编译库位置?