Swift: Почему реализация протокола по умолчанию используется при наличии переопределения? - PullRequest
1 голос
/ 13 января 2020

Мое намерение в исходном коде, из которого я извлек мой пример, состояло в том, чтобы протокол предоставил некоторые реализации по умолчанию для ряда абстрактных классов и при необходимости переопределил их в иерархиях ниже этих классов. Однако это не go, как планировалось.

Вот базовый код (взятый с игровой площадки, где я уменьшил исходный код до сути проблемы):

// protocol and default impls.

protocol MyProtocol {
    func hello()
}

extension MyProtocol {
    func hello() { print("hello from default in protocol extension") }
}

// Class hierarchy

class AbstractParent: MyProtocol {}

class Child: AbstractParent {
    func hello() { print("hello from child") }
}

(Child() as MyProtocol).hello()
Child().hello()

Запустив это, я ожидал увидеть:

hello from child
hello from child

Но вместо этого получил:

hello from default in protocol extension
hello from child

Для меня имеет смысл, что, поскольку первый вызов выполняется из преобразования Child в MyProtocol, он вызывает функцию протокола, даже если функция существует в Child, потому что во время компиляции она видит только MYProtocol.

Однако ...

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

// protocol and default impls.

protocol MyProtocol {
    func hello()
}

extension MyProtocol {
    func hello() { print("hello from default in protocol extension") }
}

// Class hierarchy

class AbstractParent: MyProtocol {
    func hello() { print("hello from abstract parent") }
}

class Child: AbstractParent {
    override func hello() { print("hello from child") }
}

(Child() as MyProtocol).hello()
Child().hello()

Теперь я получаю ожидаемое поведение:

hello from child
hello from child

Теперь, хотя мы все еще приводим к MyProtocol, он видит реализацию в Child.

Кто-нибудь может объяснить, почему добавление реализации к абстрактному родительскому классу делает эту работу?

1 Ответ

1 голос
/ 13 января 2020

У меня был разговор с коллегой, который знает Свифта лучше меня, и он сказал, что это пример довольно противоречивой известной вещи в Свифте.

Я постараюсь изложить это здесь, чтобы другие могли понять а также.

Насколько я понимаю, - когда Свифт разрешает функцию, он запускается с наиболее распространенным предком, который ее реализует. Так что в моем первом примере, где он выполняет неправильный, он начинается с AbstractParent и находит в протоколе функцию hello(). Поскольку AbstractParent не имеет реализации этой функции, а функция hello() в Child не объявлена ​​с override, компилятор Swift затем рассматривает ее как другую функцию, даже если она выглядит как переопределенная. Следовательно, он не вызывает его.

Во втором примере у нас есть hello() функция в AbstractParent и реальное переопределение в Child. Так что Swift видит реализацию и переопределение реферата и вызывает правильную.

Проблема, по сути, заключается в том, как Swift видит вещи при разрешении, и хотя мы, «средние» разработчики, думаем о любой объявленной функции в реализации как переопределение чего-либо в протоколе, на самом деле это не всегда так.

Правильно, что единственное известное решение - это сделать, как я, и добавить реализации по умолчанию к абстрактным классам.

Для моего проекта я собираюсь подумать о моих реализациях и иерархиях, чтобы выяснить, есть ли лучшее решение, но пока его нет.

...