有时在 Swift 中,为委托的类编写一个初始化程序可能会很方便JSONDecoder
或工厂方法。例如,有人可能想写
final class Test: Codable {
let foo: Int
init(foo: Int) {
self.foo = foo
}
func jsonData() throws -> Data {
try JSONEncoder().encode(self)
}
convenience init(fromJSON data: Data) throws {
self = try JSONDecoder().decode(Self.self, from: data)
}
}
let test = Test(foo: 42)
let data = try test.jsonData()
let decodedTest = try Test(fromJSON: data)
print(decodedTest.foo)
但这无法编译
Cannot assign to value: 'self' is immutable.
解决这个问题的解决方案是什么?
首先,请注意,此限制仅存在于class
es,因此示例初始化程序将按原样工作struct
s and enum
s,但并非所有情况都允许更改class
到这些类型之一。
此限制对class
初始化器是一个常见的痛点,经常出现在这个网站上(例如 https://stackoverflow.com/questions/65615354/swift-how-to-parse-data-to-class-in-extension-convenience-init)。有一个thread https://forums.swift.org/t/allow-self-x-in-class-convenience-initializers/15924/16在 Swift 论坛上讨论这个问题,并且已经开始添加必要的语言功能来使上面的示例代码编译,但这在 Swift 5.4 中尚未完成。
从线程:
Swift 自己的标准库和 Foundation 通过使类符合虚拟协议并在必要时使用协议扩展初始值设定项来解决此缺失的功能。
使用这个想法来修复示例代码的结果
final class Test: Codable {
let foo: Int
init(foo: Int) {
self.foo = foo
}
func jsonData() throws -> Data {
try JSONEncoder().encode(self)
}
}
protocol TestProtocol: Decodable {}
extension Test: TestProtocol {}
extension TestProtocol {
init(fromJSON data: Data) throws {
self = try JSONDecoder().decode(Self.self, from: data)
}
}
let test = Test(foo: 42)
let data = try test.jsonData()
let decodedTest = try Test(fromJSON: data)
print(decodedTest.foo)
效果很好。如果Test
是唯一符合的类型TestProtocol
,那么只有Test
将得到这个初始化器。
另一种选择是简单地扩展Decodable
或您的类符合的其他协议,但如果您不希望符合该协议的其他类型也获得您的初始值设定项,则这可能是不可取的。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)