Swift Protocol Extensions Method Dispatch

Leandro Pérez
6 min readFeb 20, 2017

Embrace static dispatch

Today I want to write about a bug that I found. I was researching CoreValue when I bumped into this one. CoreValue is a framework that allows you to persist value types using CoreData. The bug caused duplicate entries in the database.

The bug was interesting too because I learned something. It was using a language feature as a cloak to hide. Once I found the monster, I was able to see this language characteristic and learn about it. Then I decided to write this short story.

What did I learn?

  1. Protocol extensions have 2 types of methods. Requirement methods and Static methods (“Not Backed by Requirement”).
  2. Requirement methods use dynamic dispatch. Static methods use static dispatch.
  3. It is really important to know the difference so you don’t create bugs.

I will try to show why it’s important by explaining the bug first. You can skip this part and read about each type of methods bellow.

I am not going to use the CoreValue example because it would be a bit harder to explain. So, I am going to make up a story.

A Bug hidden among heroes

This is a story about not-very-bright heroes. Heroes are Fighters. And Fighters, they just fight(). That is the requirement. Fighters only throw a punch() when they fight. That is coded in an extension method because it is the common behavior to all Fighters:

protocol Fighter{
func fight() //Requirement
}
extension Fighter{
//Requirement
func fight(){
self.punch()
}
//Static method (it is not required in the protocol)
func punch(){
print("Fighter punch")
}
}

Heroes are not simple Fighters, they are MagicalFighters. When magical fighters punch, they cast a spell first:

protocol MagicalFighter : Fighter{
func castSpell() //Requirement
}
extension MagicalFighter{ //Requirement
func castSpell(){
print("MagicalFighter casted a spell")
}
//Static
func punch(){
self.castSpell()
print("Magical fighter punch")
}
}

There is one more detail, Heroes cast spells in their own way:

struct Hero : MagicalFighter{
func castSpell(){
print("Hero casted special spell")
}
}

Finally, a Hero is told to fight:

let gordo = Hero()
gordo.fight()

What happens when gordo fights? He is a Hero, hence a MagicalFighter. So he should cast a special spell before throwing a punch. At least that’s what I thought. But that is not the case, there is a bug hiding and messing gordo up.

What gets printed and why?

The answer is “Fighter punch”. The reason is that punch() is not backed by a requirement. It is a static method. And Static methods use static dispatch.

Wait, what? What are static methods? what is static dispatch?

Method Dispatch

It is the way of choosing what implementation will execute when you invoke a method. When the compiler resolves that at compile time it is Static dispatch. When it is resolved at runtime, it is Dynamic dispatch.

Dynamic Dispatch.

If you are used to OOP, classes and inheritance, you are familiar with dynamic dispatch. It is how method implementations are chosen and it paves the way for polymorphism. This is why I thought gordo would cast a special spell. Because I’m used to dynamic dispatch and polymorphism.

When you send a message to a class instance, dynamic dispatch chooses the implementation from the hierarchy. The implementation is chosen at runtime. Objective-C uses this mechanism. Swift uses it too, in addition to static dispatch.

Static Dispatch

Static dispatch resolves things at compile time. In Swift, value types use static dispatch because they don’t need inheritance. Global functions, outside classes or structs or enums, use static dispatch too.

Static dispatch results in better performance compromising dynamism behavior/semantics in programming. Want to take advantage of this and boost performance? Read this post by Apple and learn a trick or two to avoid dynamic dispatch.

Swift has 3 types of method dispatch. Direct or static, witness table dispatch, and message dispatch. Do you want to know more about method dispatch in Swift? Read this article.

Requirement methods

Requirement methods are the ones you declare in a protocol definition. Types that conform to the protocol will define an implementation for each requirement. It is mandatory, unless there is a common implementation available in an extension.

Protocol extensions can define common implementations for requirements. Then, all types that conform to a protocol will have a default implementation. You can decide to provide your own implementation in your conforming type. This is what they call customization point. Like castSpell() in Hero.

Requirement methods use dynamic dispatch. That’s why conforming types have the privilege of customizing requirements. If you conform to a protocol, you define an implementation for a required method. That implementation will be used. If there is a common implementation in an extension, it will be discarded.

Static methods

I use the word “static” to reference methods in a protocol extension that are not requirements of such protocol. They are not static in the traditional static func sense. I am not talking about type-level methods in structs likestatic func doSomething(). They are static because they use static dispatch. In the Heroes example, punch() is a static method in the extension of Fighter

Dave Abrahams refered to them as “not backed by requirement methods” in his famous talk, Protocol Oriented Programming. Here is the link to that moment. Using that term was kind of confusing when trying to explain all this. That’s why I used “static” instead.

Static methods use static dispatch. Hence, conforming types do not have the privilege of customizing them. If you conform to a protocol you can provide an implementation with the same signature of a static method. That will not matter. The only implementation used will be the one in the extension.

So, what does it all mean?

No polymorphism for static methods. Whatever you define in an extension, if it is not a requirement, it is static. So, forget about polymorphism for those methods. Your conforming types might have an implementation for them, but it doesn’t matter.

Going back to the example

I will finish what I started with the Heroes example now. Define punch() as a requirement of Fighter. Then you will see how gordo does what he was intended to. He will print:

“Hero casted special spell” “Magical fighter punch”

Here is another change you can make to see what happens. Provide the same requirement method fight() inside the MagicalFighter extension. That implementation will call the static method punch() inside MagicalFighter. And in turn, it will invoke the static method castSpell(), printing “MagicalFighter casted a spell”.

Final words

Static dispatch results in faster code. Dynamic dispatch gives flexibility when you think and code in terms of OOP. In general, Swift uses dynamic dispatch for pure OOP things. Like classes, protocols and closures. For value types, static dispatch is used.

I think it is important to know the difference between static and dynamic dispatch. Also, when Swift chooses one over the other. Not only because you can write better code and improve performance. But because it is easy to introduce bugs if you don’t know what you are doing.

Playground

Here is a gist with the playground code for the example. Uncomment here and there and see changes in the results.

References:

--

--

Leandro Pérez

Software Engineer, coding since 2001. I have done plenty of work in iOS with Objective-C and Swift.