Почему indexingIterator.next() использует динамическую отправку?

Почему for-in медленнее, чем while в режиме быстрой отладки? Я написал это. Спасибо тем, кто отвечает мне, я мог бы выучить Seqeunce и IteratorProtocol.

Поэтому я реализовал пользовательский тип (School ниже кода), который соответствовал Sequence. И я проверил профиль времени Xcode.

Но я ничего не могу найти свидетельство протокола введите здесь описание изображения

Но если использовать только range и for-in , профилировщик времени покажет свидетель протокола.

введите здесь описание изображения

почему 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)
}



person HyunSu    schedule 10.04.2021    source источник
comment
Ой, вы нашли ошибку в моей формулировке. IndexingIterator.next не отправляется динамически — методы, которые вызывает IndexingIterator.next, такие как Collection.formIndex, являются. Вот почему вы не видите свидетеля протокола для next, только для formIndex, subscript и т. д.   -  person Sweeper    schedule 10.04.2021
comment
@Sweeper большое спасибо! Теперь я мог понять, в чем проблема. Даже сегодня выходной, ты ответил мне. Я очень ценю с вами.   -  person HyunSu    schedule 10.04.2021


Ответы (1)


Ваш цикл for переводится как:

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

Обратите внимание, что ничто здесь не является протоколом. schools имеет тип School, iterator имеет тип SchoolIterator, все, что делает next (например, доступ к schoolList.count или индексу 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() {
    ...
}

Однако на самом деле iterator.next делает что-то вроде это:

public mutating func next() -> Elements.Element? { if _position == _elements.endIndex { вернуть nil } let element = _elements[_position] _elements.formIndex(после: &_position) вернуть элемент }

_elements определяется следующим образом:

public struct IndexingIterator<Elements: Collection> {
  
  internal let _elements: Elements

_elements может быть любым типом Collection, так что опять же, мы не знаем, к какому члену относится _elements[_position] или _elements.formIndex во время компиляции. Это Array.formIndex? Или Set.formIndex? Мы знаем это только во время выполнения, когда знаем, что такое Elements.

Рекомендуемое чтение: https://medium.com/@venki0119/method-dispatch-in-swift-effects-of-it-on-performance-b5f120e497d3

person Sweeper    schedule 10.04.2021
comment
о, я сейчас прочитал ваш комментарий после прочтения вашего ответа. Хорошо! iteratorэто статический метод! Верно? а фактически вызванный динамический метод был _elements! Ага! Теперь я действительно мог понять! Огромное спасибо!! Я действительно рад понять, что спасибо за вас. Ага. профиль времени фактически показал, что Collection.formIndex, Collection.subscript являются свидетелями протокола. Я не особо заботился об этом. Большое спасибо. Я очень ценю вас, мой профессор. ???? - person HyunSu; 10.04.2021
comment
@HyunSu «отправляется статически» и «отправляется динамически», а не «статический метод» и «динамический метод». Статические методы - это нечто совершенно другое, и dynamic методы отправляются сообщениями... Будьте осторожны с терминологией :) - person Sweeper; 10.04.2021
comment
ой! Я был так счастлив этой ошибке. спасибо за исправление моей ошибки. ???? - person HyunSu; 10.04.2021
comment
Привет, мой профессор. Не возражаете, если я попрошу вас ответить на мой новый вопрос? - person HyunSu; 16.04.2021