You’ve probably heard someone say in a code review, “Please add final to this class.”
It may look like a habit, but understanding why changes how you read code.
In short, final declares, “This class is not intended to be extended through inheritance.” It is a surprisingly substantial line that improves both performance and design safety.
Today, let’s summarize the real reasons Swift classes are often marked final.
What exactly is final?
final is a keyword that prevents inheritance and overriding.
When placed before a class, it prevents other classes from inheriting it.
Placed before a method or property, it can prevent overriding for that member alone.
final class Logger {
func log(_ msg: String) { print("[LOG] \(msg)") }
}
let logger = Logger()
logger.log("Payment completed") // Print: [LOG] Payment completed
If you write class FileLogger: Logger to inherit from Logger, you get a compiler error.
It drives home the message: “This class ends here.”
Why does adding final make it faster?
The first reason is performance. The key is the dispatch mechanism.
When inheritance remains possible, the compiler cannot know until runtime which method will actually be called, so it must look it up in a table each time.
This is called dynamic dispatch. The runtime looks up the method table (vtable) for the class and then calls the method.
With final, however, the situation changes.
Because no subclass can intervene, the compiler can be certain that “this call always targets this method.”
It can therefore call the method directly, without searching the table. This is static dispatch.
Going one step further, short methods can even be inlined by expanding their code directly at the call site.
The cost of one call is small, but if a method runs thousands of times inside a loop, the difference adds up.
The real reason is design, more than performance
Honestly, I think this matters more than performance.
It prevents the fragile base class problem.
If inheritance is open, anyone can freely interfere with the internal behavior of the parent class you created.
You may change one parent method casually, only to find that all subclasses overriding it break one after another.
Inheritance is the relationship that most severely breaks encapsulation, because subclasses can see the parent’s internals.
That is why object-oriented design principles put it this way:
Design and document for inheritance, or prohibit it. — A famous piece of advice from Effective Java.
final is a tool for putting that advice into practice in code.
Because the class was not designed for inheritance, the compiler enforces the intention not to leave it open.
Once the intention is clear, colleagues reading the code later have less to figure out.
So should every class be marked final?
Here is an important point: in Swift, a reference type does not have to be a class.
If state can be handled as a value, a struct is often the better choice. A struct has no inheritance to begin with.
So before asking, “Should I add final?”, first ask, “Does this really need to be a class?”
If you still need a class, use the following criteria.
| Situation | Decision |
|---|---|
| A class not intended for inheritance | Add final (default) |
| Performance-sensitive repeated calls | Use final to encourage static dispatch |
| A framework with clearly designed extension points | Leave final off |
| An API designed for inheritance, such as UIViewController | Do not add it |
Here is a simple way to remember the summary.
- If inheritance is not intended, add final by default
- Open only the extension points you deliberately want to expose
- If a struct works, avoid making it a class in the first place
This is how interviewers ask about it
Q. What are the benefits of adding the final keyword?
It prevents inheritance and overriding, clearly expressing the design intent. At the same time, the compiler can use static dispatch instead of dynamic dispatch, reducing call overhead and enabling inlining optimizations.
Q. Then should final be added to every class?
A class designed and documented for inheritance should remain open. However, classes without that intention are safer closed by default, and if value semantics fit, it is better to consider a struct first.
That single line of final is not merely an optimization tip; it answers how the class is meant to be used.
Starting today, whenever you create a class, ask once: “Is there a reason to leave inheritance open?” That one habit will make your code much more robust.

