Why does final (and whole-module optimization) help performance?
Mental model. If the compiler can prove nobody can replace the method, it can staple the implementation to the call site.
final tells the compiler no subclass can override the method, so it can devirtualize the call into a direct one — and then inline it. Whole-module optimization (WMO) gives the same proof across the module: if no subclass overrides it, the call can be direct.
private and internal help for the same reason — visibility limits who could override or witness.
@objc dynamic does the opposite: it forces a message send and blocks devirtualization, even if the Swift compiler could prove the type.
Common traps
- “
finalmakes every call faster.” Only calls that were actually dynamically dispatched. A static struct method gains nothing. - Enabling WMO to fix a slow build. WMO usually increases compile time while improving runtime; incremental builds are faster without it.
- Adding
dynamicfor KVO or swizzling and forgetting the cost. It is a permanent dispatch downgrade for that member.
They might follow up with
- When does devirtualization fail? When a subclass exists in another module and the compiler cannot see it, or when the member is
open. - Does
finalhurt testing? It can — you cannot subclass to fake. Prefer protocol seams for testability over openness.
Related questions
- 1.11.1 Static vs dynamic dispatch in Swift?
- 1.4.7 What is generic specialization? When does it not happen?
← Static vs dynamic dispatch in Swift? You added a method in a protocol extension and overrode it on a… →