Method Swizzling | Swift

How to change an object behavior in Runtime

Matheus de Vasconcelos
A Swift journey
6 min readJun 25, 2020

--

Photo by Ross Findon on Unsplash

First things first

Before the explanation about Method Swizzling, it’s important to understand what is the Swift Runtime, once it’s a key concept that enables language features like the Swizlling.

Runtime

The Swift Runtime is a part of the standard library that enables behaviors while the application is running. Good examples of Those behaviors are casts, reflections, and memory allocation.

At this moment developers start to realize that those behaviors are pretty common in the development. Casts, for example, are present almost every time inheritance is used, transforming superclasses into subclasses.

But how those things are Runtime features?

All the examples given are very common, for example, a cast, that is basically the keyword as in the code. But this is what a cast represents only in the compiling time, which leads to the next important concept.

Runtime X Compile time

In development, in almost all languages, the concept of those moments is very important in an application

The Compile time is the moment where the code is written and compiled, which means is the time where the code is verified to check if it’s following the lexical, syntax and semantic rules of the language.

The Runtime happens after the compiling time when the code is running, which means the code is right in terms of language rules and we can interact with it given inputs for example. Besides the interaction possibility, during the runtime, the application also deals with a lot of responsibilities, like allocate memory to variables, structs, classes, and so on.

Memory allocation

As said previously, one of the responsibilities of Runtime is Memory allocation, that is the process to separate parts of the device memory so that it is possible to save information, like a class and its properties values and methods. A good way to understand this is by looking at how it is done by the application, which can be represented by a memory table.
The following example shows a memory table of a class named SomeClass, this class has a property named value of type Int and a method named foo.

SomeClass Memory Table example

A Memory table usually has two columns, the first representing the memory address, and the second one the value that is saved in this address.
In this example the property value is in the address #110 and has saved the Int value 5000. To represent a method, there is the address #111 that has the value #230, which is an address and that address has the value 100, that can be assumed as the foo implementation.

The address #111 can be called Pointer. A pointer is a really important concept in languages like Swift, which is a C based language.
A pointer is an address that holds another address and so on. As addresses values are mutable during runtime, a pointer can point to different addresses, with different values.

Memory allocation in Swift — Going deeper

In Swift, during the compile time, the Swift compiler makes it easier to — during Runtime — allocate memory to new instances of some classes.

To do this, the compiler creates a Dispatch Table, this table is created when an object is loaded in memory, which is different from instantiating an object.

  • Load: An object is loaded in the first time it’s created. It’s important to notice that it only happens once and only to objects exposed to the Objective-C compiler (it’s possible by making the class inherit from NSObject) since the greater power of Runtime comes from it.
  • Instantiate: Every time an object is created it’s also instantiated, which means, a new part of the device memory will be separated to save this object. If the object is exposed to the Obj-C to separate the memory, it will use a Dispatch Table, as like a template, to know how much of memory will be separated and what values, it will save.

Apple’s documentation explains how it works with the methods +load and +initialize.

Dispatch Table

The Dispatch Table is created to help the application instantiate new instances during Runtime in a faster way. The following example is a dispatch table from a class named SomeClass

SomeClass Dispatch Table example

This table represents a class by its Methods. A Method is a C struct with the properties Selector and Implementation.

The Selector is a C string that represents the method name, for example, if a class named SomeClass has a method named foofunc foo() — its selector will be "SomeClass.foo".

The Implementation is a pointer to the code instructions that the method executes when called.

Method Swizzling

After all the previous concepts, understand the Method Swizzling is easy.

Method Swizzling is a feature enabled by the Runtime that changes the behavior of every single instance of some class without the use of inheritance or extension. It basically switches two methods implementations.

This change is possible in Swift because of the Dispatch Table and the Pointers. The next example shows what a swizzle does in the dispatch table to enable this change of behavior in Runtime.

SomeClass Swizzle example

The example shows a Swizzle between "SomeOtherMethod" and "SomeNMethod" which means an exchange between the implementations pointer #220 and #300. This is possible because a pointer can change where it is pointing during Runtime.

Code

Although the concept looks easy after the explanation the code can be a little scary but is just because it follows the C functions name pattern.

To make a Swizzle it’s necessary to access the dispatch table properties and exchange the IMP of two methods.

First, it’s necessary to find in which table the swizzle will happen, in other words, which class will have the behavior exchanged. There are some ways to find it, in the snippet below are some o them.

Second, it’s necessary to know which methods will exchange their implementations. The best way to find a Method is through its name, that is the Selector.

It’s possible to get the Selector by its name. The following examples show how to do it.

With the class type and the method selector — that represents the dispatch table and one value in column SEL respectively —it’s possible to get the Method, which is the full line in the table.

To get the Method use the function class_getInstanceMethod.

Since the Swizzling is an exchange, all this process must happen twice.

With all this information from the dispatch table, it’s possible to make the swizzle.

It’s possible to encapsulate the swizzle in a static function as following.

With this is possible to change every instance — of any class that inherits from NSObject— implementation between two methods.

When to use a Method Swizzle

With the theory and the code, the only thing left is to know where to use this technique.

As the Swizzling changes every instance implementation it’s a global change, so its use should take it into account. So it’s recommended to use the swizzle when all instances of some class should do something and inheritance is not an option, or when it must change a class behavior in an extension, but the function can’t be overridden.

To see a real example take a look in the below Playground.

After running the playground it’s possible to notice that the Swizzle is an important feature and very powerful, but as uncle Ben said.

“With great power comes great responsibility”

Although this feature of Runtime can enable a lot of possibilities it can cause problems too, like bug tracking, once it will change the behavior of the application during runtime, and in big projects, it can be a nightmare to control, since it will affect all the project.

--

--

Matheus de Vasconcelos
A Swift journey

iOS Developer — Apple Developer Academy Alumni | Mackenzie. Studying Unit Tests.