Does Swift have dynamic dispatch and virtual methods?
Asked Answered
S

6

27

Coming form a C++/Java/C# background I was expecting to see virtual methods in Swift, however reading the swift documentation I see no mention of virtual methods.

What am I missing?


Due to large number of views, I have decided to offer a reward for an upto date and very clear/detail answer.

Severe answered 3/6, 2014 at 11:29 Comment(0)
B
22

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.

Bourgeon answered 22/8, 2014 at 1:23 Comment(6)
I wonder if there will be KVO support on "pure Swift" classes?Patter
Cocoa-style KVO only works if the class extends NSObject. There's an Observer system baked in to Swift, I think. (Haven't tried it yet).Bourgeon
#24092785Patter
@Patter KVO relies on looking up a function based on name. That is dynamic dispatch. So if Swift uses KVO it has to use dynamic dispatch. Which again means it is NOT using vtable lookup which is what refer to as "pure" swift. I think we should dispose of the notion of pure swift. ObjC is a natural part of Swift. It was designed with ObjC in mind. It is part of Swift's DNA.Haley
In what sense "compiler will work out"? Virtual vs. static dispatch is difference in behaviour, you have some lib and you cannot tell which method will be called, because it depends on currently/previously compiler worked out? A little magic for me -- compiler can decide on performance but not on behaviour.Paddlefish
@Paddlefish The general rules are: Swift favors the static kinds (inline, static, vtable) and will choose which of these are appropriate, inline for small bodies, static if no sub-classes, otherwise vtable. Meanwhile, messaging is used if the class extends NSObject or has the @objc directive, although static-styles might still be used in these cases, and we can avoid this by declaring a method as dynamic. . Quite complex.Bourgeon
R
7

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.

Ran answered 3/6, 2014 at 11:52 Comment(2)
Is this like "virtual" in C# or like "new" in C#. E.g. at run time is the static of the variable, or the dynamic type of the instance used to decide what method is called?Severe
@IanRingrose It's like virtual from C#. It looks like Swift borrows a lot from C#; for example the where keyword on generic types.Ran
L
4
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.

Luxuriant answered 4/6, 2014 at 21:16 Comment(4)
can you check your result for ACSevere
Swift will automatically choose between inline, static dispatch, vtable dispatch. To allow dynamic dispatch required extending NSObject or adding the 'objc' directive, however in the latest version we have to mark each method as dynamic. . . I would've preferred that dynamic is the default.Bourgeon
Your example shows that Swift always statically looks up method overloads (and btw, also generics). Dynamic dispatch does work in Swift, but overloaded methods are resolved statically.Liesa
Isn't your "ac" example showing that dynamic dispatch is in fact available? Since it is defined as type A, but it outputs type C when printed by the Target class? If it were printing out A, then it would be static dispatch...Dis
H
2

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!

Huron answered 24/6, 2017 at 8:49 Comment(1)
FYI. Preferably Snoopy should be able to extend Dog. But dynamic dispatch all falls apart at that point. As dynamic dispatch will default to the protocol not the generic type. The minute Snoopy extends Dog, he is back to barking 🙁Naraka
G
2

Let’s begin by defining dynamic dispatch.

Dynamic dispatch is considered a prime characteristic of object-oriented languages. It is the process of selecting which implementation of a polymorphic operation (method/function) to call at run time, according to Wikipedia. I emhasized run time for a reason since this is what differentiates it from static dispatch. With static dispatch, a call to a method is resolved at compile time. In C++, this is the default form of dispatch. For dynamic dispatch, the method must be declared as virtual.

Now let’s examine what a virtual function is and how it behaves in the context of C++

In C++, a virtual function is a member function which is declared within a base class and is overriden by a derived class. Its main feature is that if we have a function declared as virtual in the base class, and the same function defined in the derived class, the function in the derived class is invoked for objects of the derived class, even if it is called using a reference to the base class.

Consider this example, taken from here:

class Animal
{
    public:
        virtual void eat() { std::cout << "I'm eating generic food."; }
};

class Cat : public Animal
{
    public:
        void eat() { std::cout << "I'm eating a rat."; }
};

If we call eat() on a Cat object, but we use a pointer to Animal, the output will be “I’m eating a rat.

Now we can study how this all plays out in Swift. We have four dispatch types, which are the following (from fastest to slowest):

  1. Inline dispatch
  2. Static dispatch
  3. Virtual dispatch
  4. Dynamic dispatch

Let's take a closer look at dynamic dispatch. As a preliminary, you have to know about the difference between value and reference types. To keep this answer at a reasonable length, let’s just say that if an instance is a value type, it keeps a unique copy of its data. If it’s a reference type, it shares a single copy of the data with all other instances. Static dispatch is supported by both value and reference types.

For dynamic dispatch, however, you need a reference type. The reason for this is that for dynamic dispatch you need inheritance, and for inheritance, which value types do not support, you need reference types.

How to achieve dynamic dispatch in Swift? There are two ways to do it. The first is to use inheritance: subclass a base class and then override a method of the base class. Our previous C++ example looks something like this in Swift:

class Animal {
    init() {
        print("Animal created.")
    }
    func eat() {
        print("I'm eating generic food.")
    }
}
class Cat: Animal {
    override init() {
        print("Cat created.")
    }
    override func eat() {
        print("I'm eating a rat.")
    }
}

If you now run the following code:

let cat = Cat()
cat.eat()

The console output will be:

Cat created.
Animal created.
I'm eating a rat.

As you can see, there is no need to mark the base class method as virtual, the compiler will automatically decide which dispatch option to use.

The second way to achieve dynamic dispatch is to use the dynamic keyword along with the @objc prefix. We need @objc to expose our method to the Objective-C runtime, which relies solely on dynamic dispatch. Swift, however, only uses it if it has no other choice. If the compiler can decide at compile time which implementation to use, it opts out of dynamic dispatch. We might use @objc dynamic for Key-Value Observing or method swizzling, both of which are outside the scope of this answer.

Glutinous answered 10/3, 2021 at 16:43 Comment(0)
D
-2

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.

Devonadevondra answered 3/6, 2014 at 11:47 Comment(1)
It is not necessary to explicitly specify a method as virtual in Swift, the compiler will decide whether to: a) Inline b) Use static dispatch (non-virtual) b) Use vtable dispatch (like Java) c) Use dynamic dispatch like Objective-C . . many Objective-C veterans were surprised that Swift does not default to dynamic dispatch.Bourgeon

© 2022 - 2024 — McMap. All rights reserved.