Introduction
Method dispatch is an algorithm used to select the appropriate method that needs to be invoked upon a call. The primary goal of method dispatch is to provide the program with information on where it can find the executable code for a specific method in memory.
Table of contents
Open Table of contents
Types of Method Dispatch
Compiled languages have three types of method dispatch:
- Static or direct dispatch
- Table or virtual dispatch
- Message dispatch
Static Dispatch
Static dispatch is the fastest dispatch method in Swift. Since there is no method overriding available, there is only one implementation of the method, and it resides at a single location in memory.
We can use static dispatch using keywords such as static
,final
, private
.
Static dispatch is a default method dispatch for the value types since the value types can’t be overridden.
Let’s look at some examples:
Final Keyword
Once we add the final
keyword to a class, its methods do not support overriding, and this is when static dispatch comes into play.
// MARK: Final class
final class ClassExample {
// MARK: Static dispatch
func method() {
// implementation ...
}
}
Protocol Extension
Once you add a default implementation of a protocol using an extension, its dispatch method switches to static dispatch instead of using a Witness Table.
// MARK: Prorocol Extension
extension ProtocolExample {
// MARK: Direct Dispatch
func method() {
// implementation ...
}
}
class ClassExample2: ProtocolExample {}
let classExample2 = ClassExample2()
classExample2.method()
Class Extension
When a method is implemented in an extension, it means it can’t be overridden by subclasses. In this case, there is room for static dispatch.
// MARK: Example Class Extension
class ClassExample3 {}
extension ClassExample3 {
// MARK: Direct Dispatch
func method() {
// implementation ...
}
}
let classExample3 = ClassExample3()
classExample3.method()
Access Control
We can’t access a private method outside of the class body. This means that the method can’t be overridden and uses static dispatch.
// MARK: Access Control
class ClassExample4 {
// MARK: Direct Dispatch
private func method() {
// implementation ...
}
}
Table Dispatch
Table dispatch is used when we have to deal with inheritance. This is a default type of dispatch used in Swift.
Virtual Table
For each instance of a class or subclass, a virtual table is created that contains information about implemented methods for each class and stores a reference to the appropriate implementation. The main disadvantage of virtual table dispatch is that it has lower speed than static dispatch.
Let’s look at example:
// MARK: Virtual Table
class ParentClass {
func method1() {}
func methdod2() {}
}
class ChildClass: ParentClass {
override func method1() {}
func method3() {}
}
For each instance, its own virtual table is created as follows:
Witness Table
A Witness Table is used by protocols and is created for each class that conforms to the protocol. The CPU uses this table to determine where it should look for an appropriate implementation. Each type (value and reference) that conforms to a protocol has its own Protocol Witness Table, which contains pointers to the methods of the type required by the protocol.
Let’s look at example:
// MARK: Witness Table Dispatch
protocol ProtocolExample {
func method1()
func method2()
}
class ClassExample1: ProtocolExample {
func method1() {}
func method2() {}
}
class ClassExample2: ProtocolExample {
func method1() {}
func method2() {}
}
In this case, a witness table is created for each class:
Message Dispatch
Message Dispatch is the most dynamic method dispatch style. It looks for an appropriate implementation during runtime. Because it operates during runtime, we can use Method Swizzling to change method implementations.
If you want to use message dispatch, you need to add @objc dynamic
before a method implementation.
// MARK: Message Dispatch
class ClassExample: NSObject {
@objc dynamic
func method() {}
}
class SubClassExample: ClassExample {
@objc dynamic
override func method() {}
}
let subclass = SubClassExample()
subclass.method()
The implementation of the method is searched for within SubClassExample
. If there is no implementation of this method in that class, the search continues in the parent class, and so on until it reaches NSObject
.
Let’s combine all the types into a single table:
Conclusion
In summary, method dispatch in Swift is a critical aspect of code execution, impacting performance and flexibility. By choosing the right dispatch method, developers can optimize their code, ensure adaptability, and leverage Swift’s dynamic features effectively. Understanding and mastering method dispatch is essential for building efficient and adaptable Swift applications.
Thanks for reading
If you enjoyed this post, be sure to follow me on Twitter to keep up with the new content.