“Final” keyword — Swift 5 (iOS)

Understanding the Final keyword and decoding the mystery.

Ayush Singhi
4 min readSep 26, 2020
Source: https://unsplash.com/@johnschno | Edited: Self

Okay! Let’s get right at it! For all of us, iOS developers, there comes a time when we encounter Final in the code. At that time we ask ourselves what does it do? Where to use it? How to use it? When not to use it? And many other questions start popping up in our heads.

Here, I will try to answer these questions, and hopefully, decode the mystery of final by the end.

Implications of using final

  1. It prevents overriding and inheritance.
  2. It increases the runtime performance of the code.

But what does this mean??? Let’s see!

Prevent Inheritance and Overriding

Swift like most other object-oriented languages allows a class to inherit from another class and override methods and properties declared in its superclass.

However, this is not always desirable and ideal. There are many cases where we do not want anyone or any other part of the code to change the implementation of the class — such a class is to be used the way it is and any change is undesirable.

In such cases, declaring the class as final will prevent it from being subclassed and thus, will prevent member overriding.

Compiler error occurs when we try to inherit a final class

Similarly, there are cases when we do not want to change the implementations of a few members of the class. In that case, we do not want to override these few members. The way to do so is to declare themfinal.

A User class. We want to control the behavior of generateKey() and getChars(:). So, it is declared final to restrict any changes.
A Consumer class inherited from the User class. As shown a compile-time error occurs when we try to override a final member.

Note — Please ignore edge case handling in User class member functions.

Enhance Runtime Performance

Saving time and avoiding any compiler computation at runtime makes one’s program efficient and fast. This is what makes finalmore interesting and useful for any programmer like me.

Swift being an OOP language allows defining class and inheritance. This means there can be classes and subclasses and subclasses may/may not override properties and methods of the base class.

However, when we look at this from the system’s point of view, every time a class member is referred (no matter from the subclass or the base class), the system must perform the following computations at runtime —

  1. Check if the class is inherited anywhere in the code or is itself a subclass.
  2. Check if any of the class members are overridden and the number of times. And what possible implementations are available for the referenced member.
  3. Get the correct implementation out of the multiple overrides.
  4. Indirectly call the correct implementation from a pool of implementations.

These computations increase the expressivity of the language but add to the runtime overhead as they require more time and power per call. This decreases the overall efficiency of the code.

This entire chain of events of indirectly accessing the referenced member is termed as Dynamic Dispatch by Apple. But is it necessary to go through Dynamic Dispatch all the time — NO! The question that naturally arises then is how can one get rid of dynamic dispatch? How to write efficient code?

The answer is final . When we declare a member final, it not only specifies that it cannot be inherited/overridden but it also notifies the system that — Hey! No other similar entity exists. This ensures that the system need not worry and directly implement the one defined — and this happens at Compile time. Thus, removing the runtime overhead. This is called direct access or Static Dispatch — which happens at compile time.

This means by merely adding the word final, we can shift the entire processing from runtime to compile time. Hence, increasing the runtime efficiency.

A class User is defined and is instantiated to create an object, user.

In the above snippet, when User is instantiated, Dynamic dispatched calls are sent —

  1. to variables — firstName, secondName.
  2. to methods — init(:), getChars(:), generateKey(:).

Also, in the above snippet, whenever user is accessed, it is done via Dynamic dispatched calls.

Here, even though a single User class exists, still Dynamic Dispatch is compulsorily used. This is because the system has no knowledge if a subclass of User exists, or if any member is overridden anywhere. The system can gather this information either by looking up for them (which is dynamic dispatch) or if we explicitly notify it of their non-existence (which is handled by final ).

A class User is defined and is instantiated to create an object, user.

So to sum it up — The above snippet shows a general use-case.

  1. Here the members are declared final to prevent overriding them.
  2. Declaring them final eliminates runtime overheads and implements a Static Dispatch.

This is all for now! Thank you for reading. Hope you found it informational.
Happy Coding 💻🖥

Here are some references to let you explore more about the topic —

  1. Apple blog — https://developer.apple.com/swift/blog/?id=27
  2. Different dispatches in Swift — https://heartbeat.fritz.ai/understanding-method-dispatch-in-swift-684801e718bc
  3. Swift documentation on finalhttps://docs.swift.org/swift-book/LanguageGuide/Inheritance.html #Preventing Overrides.

--

--