自 Swift 4 以来,已经获得了subscript(keyPath:)
可用于检索值的对象AnyKeyPath
及其子类。根据Swift 书,下标is available on all types
。例如,一个类的实例TestClass
可以用AnyKeyPath
类似这样的下标:
class TestClass {
let property = true
}
let anyKeyPath = \TestClass.property as AnyKeyPath
_ = TestClass()[keyPath: anyKeyPath]
这可以按预期正确编译。使用任何其他有效的子类也可以编译,包括PartialKeyPath<TestClass>
,KeyPath<TestClass, Bool>
等。此功能在协议扩展中不可用。例如,以下内容无效:
class TestClass {
let property = true
}
protocol KeyPathSubscriptable {
}
extension KeyPathSubscriptable {
func test() {
let anyKeyPath = \TestClass.property as AnyKeyPath
_ = self[keyPath: anyKeyPath] // Value of type 'Self' has no subscripts
}
}
如果我们想在协议中使用那个 keyPath 下标,我们可以将它包含在协议定义中。但是,编译器不会自动解析它:
protocol KeyPathSubscriptable {
subscript(keyPath: AnyKeyPath) -> Any? { get }
}
extension KeyPathSubscriptable {
func test() {
let anyKeyPath = \TestClass.property as AnyKeyPath // This can be any valid KeyPath
_ = self[keyPath: anyKeyPath]
}
}
class TestClass: KeyPathSubscriptable { // Type 'TestObject' does not conform to protocol 'KeyPathSubscriptable'
let property = true
}
有了这个,我们得到一个编译错误:Type 'TestObject' does not conform to protocol 'KeyPathSubscriptable'
. 为了解决这个问题,我们必须在 中包含该下标的冗余实现TestClass
:
class TestClass: KeyPathSubscriptable {
let property = true
subscript(keyPath: AnyKeyPath) -> Any? {
fatalError() // This is never executed
}
}
这解决了一致性问题并产生了目标结果,尽管它看似不必要且不合逻辑。我不确定如何,但下标实现甚至从未使用过。它正在寻找并使用它的预期实现subscript(keyPath:)
,但是如何呢?那在哪里,有没有办法在协议中使用它?为什么编译器需要它,即使它从未使用过?
此用例的上下文位于日志记录模块中。目标是对象应该能够采用特定的协议,该协议无需对对象进行额外设置,就可以提供对象的人类可读性description
,而不是许多对象的默认值,即内存地址。该协议将使用 Mirror 来获取KeyPath
对象的 s、读取值并将它们打印到控制台。它用于调试目的,不会在任何生产环境中运行。
如果我能做出任何澄清,请告诉我。如果其他人认为这可能是某种错误,我可能会将其发布给 Swift 团队。感谢所有帮助。提前致谢。
完整的要点位于此处。