У Swift есть динамическая диспетчеризация и виртуальные методы?

Приступая к созданию фона С++/Java/С#, я ожидал увидеть виртуальные методы в Swift, однако, прочитав быструю документацию, я не вижу упоминания о виртуальных методах.

Что мне не хватает?

Ответ 1

В отличие от С++, нет необходимости указывать, что метод является виртуальным в Swift. Компилятор разработает следующее:

(метрики производительности, конечно, зависят от аппаратного обеспечения)

  • Встроить метод: 0 ns
  • Статическая отправка: < 1.1нс
  • Виртуальная диспетчеризация 1.1ns (например, Java, С# или С++ при назначении).
  • Динамическая отправка 4.9ns (например, Objective-C).

Objective-C, конечно, всегда использует последнее. Накладные расходы 4.9ns обычно не являются проблемой, так как это будет представлять собой небольшую часть общего времени выполнения метода. Однако, когда это необходимо, разработчики могли бы легко вернуться на C или С++. В Swift, однако, компилятор проанализирует, какой из наиболее быстрых может быть использован, и попытайтесь принять решение от вашего имени, предпочитая встроенную, статическую и виртуальную, но сохраняя обмен сообщениями для взаимодействия Objective-C. Его можно отметить методом dynamic для поощрения обмена сообщениями.

Один из побочных эффектов этого заключается в том, что некоторые из мощных функций, предоставляемых динамической диспетчеризацией, могут быть недоступны, поскольку, как ранее предполагалось, это был случай для любого метода Objective-C. Динамическая отправка используется для перехвата метода, который, в свою очередь, используется:

  • Cocoa наблюдатели свойств.
  • Инструментальное устройство модели CoreData.
  • Аспектно-ориентированное программирование

Виды функций выше, чем те, которые предоставляются языком late binding. Обратите внимание, что, хотя Java использует диспетчеризацию vtable для вызова метода, она по-прежнему считается поздним языком привязки и, следовательно, способна к вышеуказанным функциям благодаря наличию виртуальной машины и системы загрузчика классов, что является еще одним подходом к обеспечению инструментария времени выполнения. "Чистый" Swift (без Objective-C interop) похож на С++ в том, что является прямым исполняемым скомпилированным языком со статической диспетчеризацией, тогда эти динамические функции невозможны во время выполнения. В традициях ARC мы можем увидеть больше таких функций, которые переходят на время компиляции, что дает преимущество в отношении "производительности на ватт" - важное соображение в мобильных вычислениях.

Ответ 2

Все методы являются виртуальными; однако вам нужно объявить, что вы переопределяете метод из базового класса с помощью ключевого слова override:

Из Руководство по быстрому программированию:

Переопределение

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

Чтобы переопределить признак, который в противном случае был бы унаследован, вы префикс вашего переопределяющего определения с помощью ключевого слова override. Делать это разъясняет, что вы намерены предоставить переопределение и не предоставили определение соответствия по ошибке. Преодоление случайно может привести к неожиданное поведение и любые переопределения без ключевого слова overrideдиагностируются как ошибка при компиляции вашего кода.

Ключевое слово override также запрашивает компилятор Swift, чтобы проверить, что ваш суперкласс класса (или один из его родителей) имеет которое соответствует тому, которое вы указали для переопределения. Эта проверьте, правильно ли определено ваше основное определение.

Ответ 3

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, поэтому нет динамической отправки. К сожалению.

Ответ 4

Как и 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.

Любые дальнейшие предложения будут приветствоваться!

Ответ 5

Swift был легко освоен для программистов Objective-C, а в Objective-C нет виртуальных методов, по крайней мере, не так, как вы могли бы подумать о них. Если вы ищете инструкцию по созданию абстрактного класса или виртуального метода в Objective-C здесь на SO, обычно это обычный метод, который просто генерирует исключение и выдает сообщение об ошибке. (Какой вид имеет смысл, потому что вы не должны вызывать виртуальный метод)

Поэтому, если в документации Swift ничего не говорится о виртуальных методах, я предполагаю, что, как и в Objective-C, их нет.