Приступая к созданию фона С++/Java/С#, я ожидал увидеть виртуальные методы в Swift, однако, прочитав быструю документацию, я не вижу упоминания о виртуальных методах.
Что мне не хватает?
Приступая к созданию фона С++/Java/С#, я ожидал увидеть виртуальные методы в Swift, однако, прочитав быструю документацию, я не вижу упоминания о виртуальных методах.
Что мне не хватает?
В отличие от С++, нет необходимости указывать, что метод является виртуальным в Swift. Компилятор разработает следующее:
(метрики производительности, конечно, зависят от аппаратного обеспечения)
Objective-C, конечно, всегда использует последнее. Накладные расходы 4.9ns обычно не являются проблемой, так как это будет представлять собой небольшую часть общего времени выполнения метода. Однако, когда это необходимо, разработчики могли бы легко вернуться на C или С++. В Swift, однако, компилятор проанализирует, какой из наиболее быстрых может быть использован, и попытайтесь принять решение от вашего имени, предпочитая встроенную, статическую и виртуальную, но сохраняя обмен сообщениями для взаимодействия Objective-C. Его можно отметить методом dynamic
для поощрения обмена сообщениями.
Один из побочных эффектов этого заключается в том, что некоторые из мощных функций, предоставляемых динамической диспетчеризацией, могут быть недоступны, поскольку, как ранее предполагалось, это был случай для любого метода Objective-C. Динамическая отправка используется для перехвата метода, который, в свою очередь, используется:
Виды функций выше, чем те, которые предоставляются языком late binding
. Обратите внимание, что, хотя Java использует диспетчеризацию vtable для вызова метода, она по-прежнему считается поздним языком привязки и, следовательно, способна к вышеуказанным функциям благодаря наличию виртуальной машины и системы загрузчика классов, что является еще одним подходом к обеспечению инструментария времени выполнения. "Чистый" Swift (без Objective-C interop) похож на С++ в том, что является прямым исполняемым скомпилированным языком со статической диспетчеризацией, тогда эти динамические функции невозможны во время выполнения. В традициях ARC мы можем увидеть больше таких функций, которые переходят на время компиляции, что дает преимущество в отношении "производительности на ватт" - важное соображение в мобильных вычислениях.
Все методы являются виртуальными; однако вам нужно объявить, что вы переопределяете метод из базового класса с помощью ключевого слова override
:
Из Руководство по быстрому программированию:
Переопределение
Подкласс может предоставлять свою собственную реализацию экземпляра метод, метод класса, свойство экземпляра или индекс, который иначе наследуется от суперкласса. Это называется переопределением.
Чтобы переопределить признак, который в противном случае был бы унаследован, вы префикс вашего переопределяющего определения с помощью ключевого слова
override
. Делать это разъясняет, что вы намерены предоставить переопределение и не предоставили определение соответствия по ошибке. Преодоление случайно может привести к неожиданное поведение и любые переопределения без ключевого словаoverride
диагностируются как ошибка при компиляции вашего кода.Ключевое слово
override
также запрашивает компилятор Swift, чтобы проверить, что ваш суперкласс класса (или один из его родителей) имеет которое соответствует тому, которое вы указали для переопределения. Эта проверьте, правильно ли определено ваше основное определение.
class A {
func visit(target: Target) {
target.method(self);
}
}
class B: A {}
class C: A {
override func visit(target: Target) {
target.method(self);
}
}
class Target {
func method(argument: A) {
println("A");
}
func method(argument: B) {
println("B");
}
func method(argument: C) {
println("C");
}
}
let t = Target();
let a: A = A();
let ab: A = B();
let b: B = B();
let ac: A = C();
let c: C = C();
a.visit(t);
ab.visit(t);
b.visit(t);
ac.visit(t);
c.visit(t);
Обратите внимание на ссылку self
в visit()
A
и C
. Как и в Java, он не копируется, а вместо этого self
сохраняет один и тот же тип, пока он не будет снова использован в переопределении.
Результат A, A, A, C, C, поэтому нет динамической отправки. К сожалению.
Как и Xcode 8.x.x и 9 Beta, виртуальные методы в С++ могут быть переведены в Swift 3 и 4 следующим образом:
protocol Animal: AnyObject { // as a base class in C++; class-only protocol in Swift
func hello()
}
extension Animal { // implementations of the base class
func hello() {
print("Zzz..")
}
}
class Dog: Animal { // derived class with a virtual function in C++
func hello() {
print("Bark!")
}
}
class Cat: Animal { // another derived class with a virtual function in C++
func hello() {
print("Meow!")
}
}
class Snoopy: Animal { // another derived class with no such a function
//
}
Попробуйте.
func test_A() {
let array = [Dog(), Cat(), Snoopy()] as [Animal]
array.forEach() {
$0.hello()
}
// Bark!
// Meow!
// Zzz..
}
func sayHello<T: Animal>(_ x: T) {
x.hello()
}
func test_B() {
sayHello(Dog())
sayHello(Cat())
sayHello(Snoopy())
// Bark!
// Meow!
// Zzz..
}
В целом, подобные вещи, которые мы делаем в С++, могут быть достигнуты с помощью Протокола и Общий в Swift, я думаю.
Я тоже пришел из мира С++ и столкнулся с тем же вопросом. Вышеприведенное, похоже, работает, но похоже, что это С++-способ, но не немного Swifty.
Любые дальнейшие предложения будут приветствоваться!
Swift был легко освоен для программистов Objective-C, а в Objective-C нет виртуальных методов, по крайней мере, не так, как вы могли бы подумать о них. Если вы ищете инструкцию по созданию абстрактного класса или виртуального метода в Objective-C здесь на SO, обычно это обычный метод, который просто генерирует исключение и выдает сообщение об ошибке. (Какой вид имеет смысл, потому что вы не должны вызывать виртуальный метод)
Поэтому, если в документации Swift ничего не говорится о виртуальных методах, я предполагаю, что, как и в Objective-C, их нет.