Имеет ли Swift динамическую отправку и виртуальные методы?

Coming form a C++/Java/C# background я ожидал увидеть виртуальные методы в Swift, однако, читая документацию swift, я не вижу упоминания о виртуальных методах.

Что я упустил?

5 ответов


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

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

  • встроенный метод: 0 ns
  • статическая отправка:
  • Virtual dispatch 1.1 ns (например, Java, C# или C++ при назначении).
  • Dynamic Dispatch 4.9 ns (например, Objective-C).

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

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

  • наблюдатели свойств в стиле какао.
  • CoreData модель объекта, контрольно-измерительные приборы.
  • Аспект, Ориентированный Программирование

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


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

С Swift Руководство По Программированию:

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

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

чтобы переопределить характеристику, которая в противном случае была бы унаследована, вы префикс переопределяющего определения с помощью 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() of A и C. Как и в Java, он не копируется, а вместо self сохраняет тот же тип, пока он снова не будет использоваться в переопределении.

результат A, A, A, C, C таким образом, нет динамической отправки. К сожалению.


начиная с Xcode 8.X. X и 9 Beta, виртуальные методы на C++ могут быть переведены в 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() {
    .hello()
  }
  //  Bark!
  //  Meow!
  //  Zzz..
}

func sayHello<T: Animal>(_ x: T) {
  x.hello()
}

func test_B() {
  sayHello(Dog())
  sayHello(Cat())
  sayHello(Snoopy())
  //  Bark!
  //  Meow!
  //  Zzz..
}

В общем, аналогичные вещи, которые мы делаем в C++, могут быть достигнуты с протокол и Generic в Swift, я думаю.

Я также пришел из мира C++ и столкнулся с тем же вопросом. Кажется, что это работает, но это похоже на способ C++, а не несколько быстрый способ.

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


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

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