每隔一段时间,我必须走到响应者链上才能到达一个已知类的实例。 (出于问题的目的,只是接受它。)我一直在使用while循环来执行此操作,但是我发现使用sequence()
会更酷,它可以像这样整洁地表示响应者链:
let chain = sequence(first: someView as UIResponder) {$0.next}
那是很棒的,因为到目前为止我们实际上还没有进行任何散步;该序列是惰性的,并且直到我们开始请求元素之前,匿名函数才会执行。为了证明这一点,让我用一个打印语句对代码进行检测:
let chain = sequence(first: someView as UIResponder) {r in print(r); return r.next}
好的,假设我正在寻找链中的第一个ViewController实例。我可以这样找到它:
if let vc = (chain.first {$0 is ViewController}) as? ViewController {
print(vc)
}
打印输出表明保持了惰性:我们沿着响应者链走直到到达ViewController并停止。完善!在花括号内,vc
键入为ViewController,我们开始比赛了。
这不会吸引您的注意,但是,这很丑陋。我正在测试和投放。有没有一种方法可以让我在不进行测试的情况下进行投放,而最终仍然使用ViewController?
这很优雅,可以正常工作:
for case let vc as ViewController in chain {
print(vc)
break
}
这很可爱,而且保持了懒惰-但我必须记得在最后说break
,这破坏了一切。
好的,所以当我想到这一点时,我真的充满希望:
if let vc = (chain.compactMap{ $0 as? ViewController }.first) {
print(vc)
}
从某种意义上说,它可以编译并得到正确的答案,看起来不错,但我已经失去了懒惰。整个chain
被遍历。 compactMap
会变得懒惰吗?有办法找回它吗? (或者还有其他一些优雅的方法使我完全摆脱了?)
答案 0 :(得分:1)
问题本身不是compactMap
。有两个问题:
如果要让序列延迟调用compactMap
,则需要使用lazy
。
看来first
正在阻止惰性行为。但是,如果使用first(where:)
,您会喜欢这种懒惰的行为。
因此,尽管有些微不足道,但以下功能可以满足您的需求:
if let vc = (chain.lazy.compactMap { $0 as? ViewController }.first { _ in true } ) {
...
}
或者,正如您所说,您可以在first
上实现lazyFirst
(或Sequence
):
extension Sequence {
var first: Element? {
return first { _ in true }
}
}
然后这种更简化的演绎现在仍然很懒:
if let vc = chain.lazy.compactMap({ $0 as? ViewController }).first {
...
}