Почему метод мутации в протоколе Swift бесконечно повторяется, если только не метод расширения? - PullRequest
0 голосов
/ 04 февраля 2019

Я встречал следующий код в SR-142 на bugs.swift.org .

Если в протоколе есть метод расширения, который мутирует, экземпляр класса может вызвать функцию мутированиябез проблем.

// protocol definition
protocol P { }

extension P {
    mutating func m() { }
}

// class conforming to P
class C : P {
    // redeclare m() without the mutating qualifier
    func m() {
        // call protocol's default implementation
        var p: P = self 
        p.m()
    }
}

let c = C()
c.m()

Если я внесу небольшое изменение, чтобы добавить метод к объявлению протокола:

protocol P {
  mutating func m()  // This is what I added.
}

extension P { 
  mutating func m() { } 
}

class C : P { 
  func m() { 
    var p: P = self 
    p.m() 
  }
}

let c = C() 
c.m()         // This one is calling itself indefinitely; why?

Почему c.m() продолжает вызывать себя снова и снова?

Ответы [ 2 ]

0 голосов
/ 04 февраля 2019

С вашим изменением во втором примере, включив m в определение протокола, которое инструктирует Swift использовать динамическую диспетчеризацию.Поэтому, когда вы вызываете p.m(), он динамически определяет, переопределил ли объект реализацию метода по умолчанию.В этом конкретном примере это приводит к тому, что метод рекурсивно вызывает сам себя.

Но в первом примере, если метод не является частью определения протокола, Swift будет использовать статическую диспетчеризацию, а из-за pимеет тип P, он вызовет реализацию m в P.


В качестве примера рассмотрим, где метод не является частью определения протокола (и, следовательно, не находится в«таблица свидетелей протокола»):

protocol P {
    // func method()
}

extension P {
    func method() {
        print("Protocol default implementation")
    }
}

struct Foo: P {
    func method() {
        print(“Foo implementation")
    }
}

Поскольку foo является ссылкой P и поскольку method не является частью определения P, он исключает method изпротокол свидетеля таблицы и использует статическую диспетчеризацию.В результате следующее напечатает «Реализация протокола по умолчанию»:

let foo: P = Foo()
foo.method()              // Protocol default implementation

Но если вы измените протокол, чтобы явно включить этот метод, оставив все остальное таким же, method будет включен в протокол-свидетельtable:

protocol P {
    func method()
}

Тогда следующее теперь выведет «Foo реализация», потому что хотя переменная foo имеет тип P, она будет динамически определять, имеет ли базовый тип, Foo,переопределите этот метод:

let foo: P = Foo()
foo.method()              // Foo implementation

Для получения дополнительной информации о динамической и статической диспетчеризации см. видео WWDC 2016 Понимание производительности Swift .

0 голосов
/ 04 февраля 2019

Объявляя m в протоколе и обеспечивая реализацию в вашем классе, он перезаписывает реализацию по умолчанию.

Но в первом примере, когда вы приводите свой класс в качестве протокола, он вызывает реализацию протокола по умолчанию, потому что реализация класса является его собственной, а не переписывает какой-либо из методов протокола

...