Swift. Method Dispatch

Direct Dispatch

// Inlining codestruct Point {
let x: Int
let y: Int
func draw() {
print("x: \(x), y: \(y)")
}
}
func draw(point: Point) {
point.draw()
print("point is drawn")
}
// ... initial code
let p1 = Point(x: 0, y: 0)
draw(point: p1)
// ... after inlining code
let p1 = Point(x: 0, y: 0)
p1.draw()
print("point is drawn")
// ... after inlining code
let p1 = Point(x: 0, y: 0)
print("x: \(p1.x), y: \(p1.y)")
print("point is drawn")

Table Dispatch

class Animal {
func makeNoise() { }
}
class Cat: Animal {
override func makeNoise() { print("meow") }
}
class Fox: Animal {
override func makeNoise() { print(".. what does the fox say?") }
}
let animals: [Animal] = [Cat(), Cat(), Fox(), Fox(), Cat()]for animal in animals { animal.makeNoise() } // polymorphism
class ParentClass {
func method1() { }
func method2() { }
}
class ChildClass: ParentClass {
override func method2() { }
func method3() { }
}
let obj = ChildClass()
obj.method2()
  1. take Virtual Table for the object of 0xB00 (ChildClass) by its property type
  2. take the function pointer of method2 from the table (the pointer is taken by index 1). Thus, the function pointer is 0x222
  3. jump to the address 0x222, that contains the executable code
// ... kind of runtime
method2(obj)

Protocol Witness Tables

// Polymorphism without classes and inheritanceprotocol Noisable {
func makeNoise()
}
struct Cat: Noisable {
func makeNoise() { print("meow") }
}
struct Fox: Noisable {
func makeNoise() { print(".. what does the fox say?") }
}
let noisers: [Noisable] = [Cat(), Cat(), Fox(), Fox(), Cat()]for noiser in noisers { noiser.makeNoise() } // polymorphism

Message Dispatch

class ParentClass {
dynamic func method1() { }
dynamic func method2() { }
}
class ChildClass: ParentClass {
override func method2() { }
dynamic func method3() { }
}
  1. take the hierarchy tree
  2. try to find the function pointer of method2 in ChildClass table
  3. if the function pointer is found, jump to the address to get executable code
  4. else go to ParentClass (super of ChildClass) and repeat 2–4
  5. do this until the function pointer is found or the root class of the hierarchy is reached
class A {
func doAction() { } // table dispatch
}
class B: A {
final override func doAction() { } // static dispatch
}
let a: A = A()
a.doAction() // will be called via table dispatch
let b1: A = B()
b1.doAction() // will be called via table dispatch
let b2: B = B()
b2.doAction() // will be called via static dispatch

final

// denies any inheritance from the class
final
class Animal {
// makes the method called via static dispatch
final
func move() { }
}

dynamic

class Animal {
// message dispatch at Objective-C runtime
dynamic func move() { }
}

@objc

class Animal {
@objc func move() { } // is visible at Objective-C runtime
}
class Animal { }extension Animal {
@objc func move() { }
}
class Lion: Animal {
override func move() { }
}

@nonobjc

@objc final

class Animal {
// direct dispatch, visible for Objective-C runtime
@objc final func move() { }
}

SR-103 (Swift bug, protocols)

protocol Greetable {
func sayHi()
}
extension Greetable {
func sayHi() {
print("from protocol")
}
}
func greetings(greeter: Greetable) {
greeter.sayHi()
}
class Person: Greetable { }class Employee: Person {
func sayHi() {
print("from employee")
}
}
greetings(greeter: Employee())
// from protocol, however Employee has own implementation

Resources

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store