Method dispatch in Swift

Method dispatch is a term referring to mechanisms by which the program determines which operation should be executed (by operation, I mean a set of instructions). There are times we expect a method behavior to be determined only at runtime. This motivation give rise to different mechanisms of dispatching a method, each of which has its own pros and cons.

Static dispatch

Dynamic dispatch

Table dispatch

Message dispatch

Swift: What is what?

Given a function, what kind of dispatch is it using? Where is the proof?

Methodology to determine dispatch mechanism

As a skeptic, I am more interested in the second part of the question. It is easy to come up with a hypothesis but testing it is not straightforward all the time. After hours googling, I happened to know the SIL documentation which reasonably explains the presence of dispatch strategies. Here is a brief summary:

(1) If a function uses table dispatch, it appears in the vtable (or witness_table for protocols).

sil_vtable Animal {
	#Animal.makeSound!1: (Animal) -> () -> () : main.Animal.makeSound() -> ()	// Animal.makeSound()
  ......
}

(2) If a function is dispatched via message, the keyword volatile should be present in the invocation. Also, you will find the two marker foreign and objc_method, indicating that the function is invoked using Objective-C runtime. Refer: here.

%14 = class_method [volatile] %13 : $Dog, #Dog.beWild!1.foreign : (Dog) -> () -> (), $@convention(objc_method) (Dog) -> () // user: %15

(3) If there is no evidence of the two cases above, the answer is static dispatch.

Trivial cases

extension Animal {
	func eat() { }
	@objc dynamic func getWild() { }
}
class Dog: Animal {
	override func eat() { }	// Compiled error!
	@objc dynamic override func getWild() { }	// Ok :)
}

Other cases

protocol Noisy {
	func makeNoise() -> Int	// TABLE
}
extension Noisy {
	func makeNoise() -> Int { return 0 }	// TABLE
	func isAnnoying() -> Bool { return true }	// STATIC
}
class Animal: Noisy {
	func makeNoise() -> Int { return 1 }	// TABLE
	func isAnnoying() -> Bool { return false } // TABLE
	@objc func sleep() { }	// Still TABLE
}
extension Animal {
	func eat() { }	// STATIC
	@objc func getWild() { }	// MESSAGE
}
let animal1 = Animal()
print(animal1.isAnnoying())	// Value: false
let animal2: Noisy = Animal()
print(animal2.isAnnoying())	// Value: true
%9 = class_method %8 : $Animal, #Animal.sleep!1 : (Animal) -> () -> (), $@convention(method) (@guaranteed Animal) -> () // user: %10

What are the principles?

Another key take-away is that explicity is better. Implicit inference (like extensions with @objc) is subject to change.

Here is the summary of some common cases. You are recommended to double check by reading the SIL generated.

Direct Table Message
Explicitly enforced final, static dynamic
Value type all methods
Protocols extensions initial declaration
Class extensions initial declaration extensions with @objc
Table 1. Summary of method dispatch in Swift (reading from top to bottom). Note that some cases such as class extensions with @objc dynamic is already mentioned aboved in explicitly enforced. Many blog posts divide classes into 2 groups: NSObject subclasses vs. (regular) classes. Though NSObject inherits a number of methods that were written on top of Objective-C runtime, I see no reason to separate them.

Conclusion

In this post, we got to know what method dispatch is and different types of dispatch in Swift. We dived into some examples to understand how Swift resolves a specific function. Also, by reading the SIL, we could be able to collect proofs for an assumption on which dispatch a function should follow.

Finally, things might be different in later Swift versions. Don’t forget to check the validity of this post 😇.

Reference

  1. Method Dispatch in Swift - by Brian King
  2. The Case for Message Passing in Swift - by Michael Buckley
  3. [swift] Dynamic keyword - by Srdan
  4. Swift Intermediate Language (SIL)
  5. Friday Q&A 2014-07-04: Secrets of Swift’s Speed
  6. The Swift Programming Language (Swift 4): Declaration Modifiers