Method dispatching mines in Swift

Ahmed Salah
4 min readDec 24, 2022

--

First of all, let’s do a quick test. What do you think will be the output of this program?

class A {
func execute(ind: Int = 0) {
print("A: \(ind)")
}

}

class B: A {
override func execute(ind: Int = 1) {
print("B: \(ind)")
}
}

let instance: A = B()
instance.execute()

The output is “B: 0”

Please continue reading (I promise to keep it short 😊) to understand how did this happen.

Method Dispatch

Method dispatching is when a program is executing and faces a method call. The program needs to dispatch to the address of this method’s implementation. Sometimes we expect that the implementation for a method is determined only at runtime.

Swift provides multiple dispatching mechanisms sorted by speed:

  1. Static Dispatch
  2. V-Table Dispatch
  3. Message Dispatch

In this article, we will be referring to the V-Table Dispatch and Message Dispatch as “Dynamic Dispatch”. They both have the same behavior of identifying which implementation to be used at runtime but with different performances.

We will be more focusing on the differences between Static and Dynamic dispatch.

Dynamic Dispatch

This is a mechanism of choosing the method implementation to be used at runtime. So, when the program hits a function call, it starts searching for the correct implementation that should be executed and jumps to it. This searching step makes it an overhead which makes it slower.

Why is it being used? Because it is flexible. Thanks to dynamic dispatch, developers can now define the method only one time and provide multiple implementations for it. The compiler with the help of the dynamic dispatch can now choose the correct implementation for this method.

Most of us are using the power of dynamic dispatch in our daily development without noticing it; It allows the existence of Polymorphism and the Protocol pattern. As mentioned, in these cases you only need to define the method in 1 place and implement it in multiple classes and the dynamic dispatch will take care of you.

Static Dispatch

Sometimes referred to as “Direct dispatch”. At compile time the compiler already knows which implementation will be used if the method is called. So, when the function is called the program jumps directly to the address that was generated while compiling.

Deciding the dispatching mechanism

Swift always tries to prioritize static dispatching for better performance.

Examples

Value Types

Since structs and enums are value types, which don’t support inheritance, all method calls will use static dispatch. The compiler knows there will be only 1 implementation for each method at runtime.

Protocol

All methods defined in the protocol itself will be dispatched dynamically but any method defined inside a protocol's extension will be dispatched statically.

protocol Shape {
func draw() // Dynamic
}

extension Shape {
func area() {
print("area") // Static
}
}

What do you think about this? pretty straightforward and easy to understand, correct? Unfortunately, it is not. Here comes our first mine

Mine #1

protocol Shape {
func draw()
}

extension Shape {
func draw() {
print("Shape")
}
func area() {
print("Shape")
}
}

class Circle: Shape {
func draw() {
print("Circle")
}
func area() {
print("Circle")
}
}

let circle: Shape = Circle()
circle.draw() // "Circle", Dynamically dispatched
circle.area() // "Shape", Statically dispatched

Here the area method is statically dispatched but we also implemented the same method in the Circle class which will take no effect as the compiler will select Shape's default implementation to be executed.

This result is too weird and may take hours to debug if you’re not aware of the static/dynamic dispatching

Class

As a class can be inherited, the default dispatching mechanism is Dynamic dispatch.

It’s the developer’s responsibility to help the compiler optimize its performance. How?

The compiler will dispatch the method statically in any of these cases:

  1. The class is marked final
  2. The method is marked private or final. As a private method can’t be overridden by subclasses
  3. The method is defined in an extension

Note that when annotating a method with @objc or dynamic will override the dispatching mechanism to be dynamic dispatch

class A {
func foo() { } // Dynamic
private func bar() { } // Static
final func bas() { } // Static
}

extension A {
func doWork() { } // Static
}

final class B {
func doWork() { } // static
}

So a nice to do would be to mark any new class with final upon creation and remove it when inheritance is needed. Also, mark all methods private and remove them when needed.

Mine #2

Back to the snippet from the top

class A {
func execute(ind: Int = 0) {
print("A: \(ind)")
}

}

class B: A {
override func execute(ind: Int = 1) {
print("B: \(ind)")
}
}

let instance: A = B()
instance.execute()

How did this happen? Here we are calling A's method which will use its default value (0) but will dynamically dispatch the implementation only. You will end up with B's implementation and A's method definition.

Where to go

If you want to deep dive you can read this article which I have used for inspiration.

You can connect with me on Linkedin.

--

--