2016-11-05 3 views
4

Мне интересно, почему следующее не распечатывает то, что, как я думаю, должно.Расширения протокола и подклассы

/* Fails */ 
protocol TheProtocol { 
    func update() 
} 

class A: TheProtocol { 
} 

class B : A {} 

extension TheProtocol { 
    func update() { 
     print("Called update from TheProtocol") 
    } 
} 

extension TheProtocol where Self: B { 
    func update() { 
     print("Called update from B") 
    } 
} 

let instanceB = B() 
instanceB.update() 

let instanceBViaProtocol:TheProtocol = B() 
instanceBViaProtocol.update() 

Это напечатает следующее:

Called update from B 
Called update from TheProtocol // Why not: Called update from B (extension) 

Я особенно интересно, почему

instanceBViaProtocol.update() 

Не выполняет обновление() в расширении на TheProtocol:

extension TheProtocol where Self: B { 
    func update() { 
     print("Called update from B") 
    } 
} 

Я бы подумал, что это будет так как B наследует от A, который принимает TheProtocol, поэтому я бы подумал, что B тогда неявно примет TheProtocol. Переход от принятия протокола к B из A дает ожидаемый результат.

protocol TheProtocol { 
    func update() 
} 

class A { // Remove TheProtocol 
} 

class B : A, TheProtocol {} // Add TheProtocol 

extension TheProtocol { 
    func update() { 
     print("Called update from TheProtocol") 
    } 
} 

extension TheProtocol where Self: B { 
    func update() { 
     print("Called update from B") 
    } 
} 

let instanceB = B() 
instanceB.update() 

let instanceBViaProtocol:TheProtocol = B() 
instanceBViaProtocol.update() 

Результат:

Called update from B 
Called update from B 

Я посмотрел на https://medium.com/ios-os-x-development/swift-protocol-extension-method-dispatch-6a6bf270ba94#.6cm4oqaq1 и http://krakendev.io/blog/subclassing-can-suck-and-heres-why, но я не мог понять это. Не применяются ли методы расширения для подклассов объектов, принимающих протокол?

+0

Изменить 'расширение TheProtocol где Self: B {' to 'extension TheProtocol где Self: A {' и посмотреть, объясняет ли это что-то для вас. –

+0

Спасибо за подсказку. – user6902806

ответ

0

Ответ Метод отправки + a Bug in Swift.

Метод отправки - это механизм, используемый компилятором для выбора реализации, выполняемой при вызове метода. Swift использует 3 вида отправки метода. Вы можете прочитать об этом here

Способ доставки определяется типом ссылки, а не типом экземпляра. В вашем случае ссылочным типом является TheProtocol.

let instanceBViaProtocol:TheProtocol = B() 
instanceBViaProtocol.update() 

У вас есть расширение протокола, которое определяет общее поведение для метода требований. В этом случае используется динамическая отправка. Это означает, что следует использовать реализацию, объявленную в B. Но есть a bug in Swift, что вызывает проблему.

Для каждого типа Swift используется таблица свидетелей для регистрации реализаций, используемых для динамической отправки. Эта ошибка приводит к тому, что класс B не может зарегистрировать свою реализацию update() в таблице Witness таблицы TheProtocol. И когда обновление отправляется через таблицу TheProtocol, используется неправильная реализация.

Здесь у вас есть пример с некоторыми изменениями. Обратите внимание, что если вы объявляете обновление в суперклассе и переопределяете его в подклассе, он работает так, как ожидалось. Это самый лучший способ увидеть ошибку.

protocol TheProtocol { 
    func update() 
} 

class A: TheProtocol { 
    func update(){ 
     print("Implementation of A") 
    } 
} 

class B : A { 
    override func update(){ 
     print("Implementation of B") 
    } 
} 

//All those who conform to TheProtocol will execute this. 
extension TheProtocol { 
    func update() { 
     print("Common: TheProtocol") 
    } 
} 
extension TheProtocol where Self: B { 
    func update() { 
     print("Common: TheProtocol for B's") 
    } 
} 
extension TheProtocol where Self: A { 
    func update() { 
     print("Common: TheProtocol for A's") 
    } 
} 


let instanceBViaProtocol:TheProtocol = B() //It prints "Implementation of B" 
instanceBViaProtocol.update() 

Надеюсь, это ответит на вопрос.

https://www.raizlabs.com/dev/2016/12/swift-method-dispatch/ имеет удивительное объяснение по методу отправки в быстром.

Here вы можете прочитать короткую вещь, которую я написал о методе отправки в расширениях протокола.

Смежные вопросы