Does Swift have dynamic dispatch and virtual methods?

自作多情 提交于 2019-11-29 01:14:23

Unlike C++, it is not necessary to designate that a method is virtual in Swift. The compiler will work out which of the following to use:

(the performance metrics of course depend on hardware)

  • Inline the method : 0 ns
  • Static dispatch: < 1.1ns
  • Virtual dispatch 1.1ns (like Java, C# or C++ when designated).
  • Dynamic Dispatch 4.9ns (like Objective-C).

Objective-C of course always uses the latter. The 4.9ns overhead is not usually a problem as this would represent a small fraction of the overall method execution time. However, where necessary developers could seamlessly fall-back to C or C++. In Swift, however the compiler will analyze which of the fastest can be used and try to decide on your behalf, favoring inline, static and virtual but retaining messaging for Objective-C interoperability. Its possible to mark a method with dynamic to encourage messaging.

One side-effect of this, is that some of the powerful features afforded by dynamic dispatch may not be available, where as this could previously have been assumed to be the case for any Objective-C method. Dynamic dispatch is used for method interception, which is in turn used by:

  • Cocoa-style property observers.
  • CoreData model object instrumentation.
  • Aspect Oriented Programming

The kinds of features above are those afforded by a late binding language. Note that while Java uses vtable dispatch for method invocation, its still considered a late binding language, and therefore capable of the above features by virtue of having a virtual machine and class loader system, which is another approach to providing run-time instrumentation. "Pure" Swift (without Objective-C interop) is like C++ in that being a direct-to-executable compiled language with static dispatch, then these dynamic features are not possible at runtime. In the tradition of ARC, we might see more of these kinds of features moving to compile time, which gives an edge with regards to "performance per watt" - an important consideration in mobile computing.

All methods are virtual; however you need to declare that you are overriding a method from a base class using the override keyword:

From the Swift Programming Guide:

Overriding

A subclass can provide its own custom implementation of an instance method, class method, instance property, or subscript that it would otherwise inherit from a superclass. This is known as overriding.

To override a characteristic that would otherwise be inherited, you prefix your overriding definition with the override keyword. Doing so clarifies that you intend to provide an override and have not provided a matching definition by mistake. Overriding by accident can cause unexpected behavior, and any overrides without the override keyword are diagnosed as an error when your code is compiled.

The override keyword also prompts the Swift compiler to check that your overriding class’s superclass (or one of its parents) has a declaration that matches the one you provided for the override. This check ensures that your overriding definition is correct.

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);

Note the self reference in the visit() of A and C. Just like in Java it gets not copied over but instead self keeps the same type until it is used in an override again.

The result is A, A, A, C, C so there's no dynamic dispatch available. Unfortunately.

As of Xcode 8.x.x and 9 Beta, virtual methods in C++ might be translated in Swift 3 and 4 like this:

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
  //
}

Give it a try.

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..
}

In sum, the similar things we do in C++ can be achieved with Protocol and Generic in Swift, I think.

I also came from C++ world and faced the same question. The above seems to work, but it looks like a C++ way, not somewhat Swifty way, though.

Any further suggestions will be welcome!

Swift was made to be easy to learn for Objective-C programmers, and in Objective-C there are no virtual methods, at least not in the way that you might think of them. If you look for instruction on how to create an abstract class or virtual method in Objective-C here on SO, usually it's a normal method that just throws an exception and crashes the app. (Which kinda makes sense, because you're not supposed to call a virtual method)

Therefore if Swift documentation says nothing about virtual methods, my guess is that, just as in Objective-C, there are none.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!