It's important to know the difference between static and dynamic dispatch for optimizing the code and better performance.
In this context, dispatching just refers to the action of finding the right function to call.
When you define a method inside the struct or final class, the compiler will remember its definition and execute it every time a call to that method is encountered.
Consider the following example:
struct Cat {
func speak() {
print("meow")
}
}When we execute the above code snippet and call the speak function on Cat reference, the compiler knows the right function the call should dispatch to in compile time.
In static dispatch, the compiler has visibility which implementation of method going to be executed. And during static dispatch the compiler replace whole chain of static dispatch with direct single implementation.
Then a question arise, why compiler won't prevent us from creating infinite recursion loop while using static dispatch ?
As above lines said, function calls will be replaced with single implementation, so what's the reason ?
struct Dog {
func speak() {
speak()
}
}
let dog = Dog()
dog.speak()I believe, Swift don't do deep analysis for static dispatch methods.
Dynamic dispatch, on the other hand, happens at runtime. It's like deciding what action to take when you are dealing with an object whose specific type isn't known until the program is actually running.
This is common when working with classes and inheritance.
Let's use the static dispatch example with class type. And create two more derived classes 'HouseCat' and 'Lion'.
class Cat {
func speak() {
print("Meow !")
}
}
class HouseCat: Cat { }
class Lion: Cat {
override func speak() {
print("Roar !")
}
}And create a function that use base type 'Cat' as an argument.
func sound(cat: Cat) {
cat.speak()
}In above code snippets, the compiler not able to take decision at compile time which implementation of 'Cat' object will invoke.
It can only be determined at run time. That's why it's called dynamic dispatch.
It is mechanism used in programming language to support dynamic dispatch.
Whenever you declare a class, compiler add a hidden member variable to the class that points to array of 'pointers to functions' called virtual method table, dispatch table or vtable.
The table contains an entry for each method accessible by class and store a pointer to its definition. Entries in the vtable can point to either method declared in the class itself or the overridden methods in derived class.
These pointers are used at runtime to invoke the appropriate function implementation, because at compile time it may not yet to be known if the base function is to be called or derived one.
During runtime, referenced type's virtual method table will contain the addresses of the referred object's dynamically bound methods. Method calls are performed by fetching the method's address from the referenced type's virtual method table.
Typically, the compiler creates a separate virtual method table for each class. When an object is created, a pointer to this table, called virtual table pointer, vpointer or VPTR, is added as a hidden member of this object.
As such, the compiler must also generate 'hidden' code in the initializers of each class instance to initialize a new object's virtual table pointer to the address of its virtual method table.
- Classes by default dynamic dispatch their methods. However, declarations with internal access are only visible within the module where they are declared. Because Swift normally compiles the files that make up a module separately, the compiler cannot ascertain whether or not an internal declaration is overridden in a different file. However, if Whole Module Optimization is enabled, all the module is compiled together at the same time. This allows the compiler to make inferences about the entire module together and infer final on declarations with internal if there are no visible overrides.
We can also achieve polymorphism using protocol oriented programming using protocols and structs.
Also, whenever a struct adopt protocol, it takes part in dynamic dispatch without V-table which needs inheritance. Instead, protocol witness table is used to find the right implementation.
Extensional container is used to obtain, protocol witness table for type.
