Protocol oriented programming, pt. 3

5 min readOct 24, 2019


In this part we will look at how variables of generic types are stored and copied and how method dispatch works with them.

Non-generic version

Pretty basic code. `drawACopy` takes a parameter of type `Drawable` and call its `draw` method — that’s it.

Generic version

Let’s look at the generic version of upper code:

Well, seems like nothing has changed. We can still just call the function `drawACopy` like it non-generic version and nothing more but the interesting things, as usual, are under the hood.

Generic code supports three major things:

1. static polymorphism (also known as parametric polymorphism)

2. one type per call context (`T` generic type is defined at compile time)

Let’s look at this point through an example:

The most interesting part is happening when we call `foo` function. The compiler knows the exact type of `point` variable — it is just `Point`. Furthermore, the type `T: Drawable` in function `foo` can be freely deduced by compiler since we pass the variable of known type `Point` to that function: `T = Point`. All types are known at compile-time and compiler can perform all its great optimizations — the most significant is inlining the `foo` call.

Compiler just inline the `foo` call by its implementation and deduce the type of `bar`’s `T: Drawable` too. In other words first compiler inline the `foo` call with `T = Point`, then inline the result of previous inlining — method `bar` with `T = Point`.

Implementation of Generic Methods

Inside of `drawACopy` Swift uses Protocol Witness Table (which contain all method implementations of T) and Value Witness Table (which contain all lifecycle methods for an instance of `T`). In pseudo code it looks like this:

`T.PWT` and `T.VWT` are associated types of `T` — like type aliases but better. `Point.pwt` and `Point.vwt` are static properties.

Since, in our example, the `T` is `Point`, the `T` is well defined hence the creation of existential container isn’t required. In previous non-generic version of `drawACopy(local: Drawable)` the existential container was created and this was necessary — we’ve studied this in the second article.

The Value Witness Table is required in functions because of creating the argument. As we know, the arguments in Swift are passed by values, hence need to be copied, and `copy` method of some type belongs to Value Witness Table of that type as other lifecycle methods: `allocate`, `destruct` and `deallocated`.

The Protocol Witness Table is required in generic functions because of using the methods of the argument of generic type.

Generic or Non-Generic?

Is using generic types makes code execution any faster than using just protocol types? Is `func foo<T: Drawable>(arg: T)` any faster then `fun foo(arg: Drawable)`?

Well we notice, that generic code brings the more static form of polymorphism. It also enables the compiler optimization called “Specialization of generics”. Let’s take a look.

Again, we got the same code:

Specialization of generics creates a type specialized version of function per type based on generic version of that functions. For example, if we call `drawACopy` with an argument of type `Point` then compiler will create a specialized version of that function e.g. `drawACopyOfPoint(local: Point)` and we got:

Which can be reduced by aggressive compiler optimizations to this:

All these tricks are available because generic functions can only be invoked if all generic types are defined — in `drawACopy` the generic type (`T`) is perfectly defined.

Generic Stored Properties

Consider the simple `struct Pair`:

When we use it in this way, we got 2 heap allocations (the exact memory state were explained in the second part) but we can avoid it by using generics.

The generic version of `Pair` looks like this:

Since in generic version the `T` type is defined, the types of `fst` and `snd` are the same and also defined. Because type is defined, the compiler can allocate the specified amount of memory for that two properties `fst` and `snd`.

More details about the specified amount of memory.

When we work with non-generic version of `Pair` the type of `fst` and `snd` are `Drawable`. Any type can conform to `Drawable`, even if it takes 10 KB of memory. So then Swift could not infer the size of that type and will use the universal memory layout called **existential cotainer**. Any type could fit in that container. In case of generics the type is well known then the actual size of properties is also known and Swift can create the specialized memory layout. For example (generic version):

The `T` type is `Point`. `Point` takes N bytes of memory and in `Pair` we got two of them. So Swift will allocate 2 * N amount of memory and place the `pair` into this.

So with generic version of `Pair` we got rid of extra heap allocations because types are well known and could be layout specifically — no need of creating of universal memory layouts because all is known.

Sum Up

1. Specialized Generics — Value types
have the best performance because:

+ no heap allocations on copying
+ generic code is like you write the function for that specific type
+ no reference counting
+ static method dispatch

2. Specialize Generics — Reference types
have the medium performance because:

+ heap allocations on creating instances
+ reference counting
+ dynamic method dispatch through Virtual Table

3. Unspecialized Generics — Small Value

+ no heap allocations — value fits in value buffer of existential container
+ no reference counting (since no heap allocations)
+ dynamic dispatch through Protocol Witness Table

4. Unspecialized Generics — Large Values

+ heap allocations — value cannot fits in value buffer размещение на heap
+ reference counting
+ dynamic dispatch through Protocol Witness Table

These articles don’t show that classes are bad, structs are good and structs with protocols with generics are best. What we are trying to say is that as a programmer this is your responsibility to choose the instruments for yout tasks. The classes are really good when you need to store large values and have reference semantics. The `structs` are best for small values and when you need value semantics. The protocols best fits with generics and structs, etc. All instruments are specific to a task that you are solving and have good and bad sides.

Also don’t pay for dynamism when you don’t need it. Choose fitting abstraction with the least dynamic runtime requirements.

+ struct types — value semantics
+ class types — identity
+ generics — static polymorphism
+ protocol types — dynamic polymorphism

Use indirect storage to deal with large values.

And don’t forget — this is your responsibility to choose the right tools. Good luck!

Thanks for reading! And join our website for more articles:




Mobile development: iOS, Android, Java, Swift, Objective-C, Design (Graphics, Web, Icon, Logo, UI/UX elements)