为什么 indexingIterator.next() 使用动态调度?

问题描述

Why is for-in slower than while in swift debugging mode? 我写了这个。 感谢回答我的人,我本可以学习 SeqeunceIteratorProtocol

所以我实现了符合 School自定义类型(Sequence 下面的代码)。 我检查了 Xcode 时间配置文件

但我找不到任何协议见证

enter image description here

但如果只使用 rangefor-in ,时间分析器显示协议见证

enter image description here

为什么 indexingIterator.next() 使用 动态方法 而不是 School ? 我认为即使struct符合protocol,如果struct类型中的变量使用protocol方法,该方法将是静态方法。如果我错了,你能告诉我哪里错了吗?

⬇️School 代码

struct SchoolIterator: IteratorProtocol {
    
    private var schoolList: School
    var idx = 0
    init(_ school: School) {
        self.schoolList = school
    }
    
    mutating  func next() -> String? {
        defer { idx += 1 }
        guard schoolList.count-1 >= idx
            else { return nil }
        
        return schoolList[idx]
    }
}

struct School: Sequence {
    fileprivate var list = Array(repeating: "school",count: 100000)
    var count: Int { return list.count }
    
    subscript(_ idx: Int ) -> String? {
        guard idx <= count-1
            else { return nil }
        return list[idx]
    }
    func makeIterator() -> SchoolIterator {
        return SchoolIterator(self)
    }
}
var schools = School()
for school in schools {
    print(school)
}


解决方法

您的 for 循环转换为:

var schools = School()
var iterator = schools.makeIterator()
while let school = iterator.next() {
    print(school)
}

注意这里没有什么是协议。 schools 属于 School 类型,iterator 属于 SchoolIterator 类型,next 所做的一切(例如访问 schoolList.count,或 { {1}}) 也处理结构。关键是编译器可以准确地找出您的意思是哪个成员,因为它的(编译时)类型是一个结构体。无需查找见证表。

比较一下,例如

schoolList

编译器如何将调用分派到 func f<S: Sequence>(_ s: S) { for thing in s { ... } /* var iterator: S.Iterator = s.makeIterator() while let thing = iterator.next() { ... } */ } f(School()) f(1..<100) ?我特意添加了类型注释以清楚说明发生了什么 - 这一次,编译器不知道您指的是哪个 iterator.next()。是next吗?还是IndexingIterator.next()?还是SchoolIterator.next()?请记住,我可以使用任何类型的 SomeOtherIterator.next() 调用 f!这就是为什么它需要在运行时查找 Sequence 的实际类型的见证表 - 无法确定要调用哪个 S.Iterator

至于为什么 next 使用动态分派,嗯,乍一看,似乎都是结构体:

for i in 0..<100

然而,let range: Range<Int> = 0..<100 var iterator: IndexingIterator<Range<Int>> = range.makeIterator() while let i = iterator.next() { ... } 实际上做了类似 this 的事情:

public mutating func next() -> Elements.Element? { 如果 _position == _elements.endIndex { 返回 nil } 让元素 = _elements[_position] _elements.formIndex(after: &_position) 返回元素 }

iterator.next 的定义如下:

_elements

public struct IndexingIterator<Elements: Collection> { internal let _elements: Elements 可以是任何类型的 _elements,所以同样,我们不知道编译时 Collection_elements[_position] 指的是哪个成员。是_elements.formIndex吗?还是Array.formIndex?我们只在运行时知道 Set.formIndex 是什么。

推荐阅读:https://medium.com/@venki0119/method-dispatch-in-swift-effects-of-it-on-performance-b5f120e497d3