Using @objc in Swift is not innocent, but final enters nitro mode
Leon Dobnik
Mobile software architect (iOS) | Corporate mobility | Corporate iOS secure apps | Banking | Team leadership
I have been recently following advanced Swift course led by Michael Rudowsky. In its discussion boards there has been a topic about Swift's protocol and option of using @objc in the protocol. I have warned developers that they shouldn't use @objc annotation unless really needed due performance impact. Some of them tried to measure it by calling function annotated with @objc a few times and performance difference has been really minor - about 1%. But I was sure that there is more behind this and I started investigating... and it really is. What is it?
The shortest possible answer is Method dispatch. Method dispatch is an algorithm used to decide which method will be invoked in response to a "message". This algorithm is for example needed when inheritance is used and a method is called on object. The programming languages may use static dispatch (it is known to a compiler which method will be run - compile time) or dynamic dispatch (it is known at run-time which method will be called). Now Objective-C uses only dynamic dispatch, whereas Swift is using dynamic and static dispatch. There are also differences in implementation details, compiler optimisations,... Let me explain briefly what happens when you invoke method in Objective-C: the algorithm checks if the object has the method - if it does it invokes it, if it doesn't it puts the invocation of that method to its parent object. The "testing" can go up to NSObject class and if even that one doesn't have required method an exception is thrown. But what is happening in Swift? If possible compiler will set static dispatch, otherwise a dynamic dispatch will be used. If you are interested even more about this topic, check out this video. So what happens when you use @objc annotation? Well your "Swift" class conforms to NSObject and therefore Objective-C runtime will be used.
To test performance of method dispatch I have implemented three very simple cases: one for static dispatch (method has final modifier - no inheritance), one for swift dynamic dispatch and one for "swift over objective-c" dynamic dispatch. Swift will use static dispatch only when it can be 100% sure, that there is no need for dynamic dispatch - for example a method is marked as final (I will talk about this later). For the second two (to force searching for the methods - hint: Objective-C algorithm) I have put the invoked method into the parent class, but I have call it on his sub-sub class (inheritance level 2).
The implementation for Objective-C is the same, the only difference is that there @objc annotation used and class conforms to NSObject. Then I have prepared a swift test-case and invoked methods for each case 1.000.000 times.
I have run results several times on iPhone 6 and iPhone 5 and results are the same as shown in the picture above:
- the static dispatch is 427% faster than dynamic dispatch in swift and 536% faster than "swift over objective-C" (for this case)
- the performance impact (degradation) is 25% when using @objc on swift's class (for this case)
It is also interesting that depth of inheritance impacts on the method dispatch. I have changed the test implementation by invoking the method 1.000.000 times on the class which actually implements the method:
From the picture above we can see:
- the static dispatch is 18% faster than swift's dynamic dispatch and 36% faster than @objc (for this case)
- the performance impact (degradation) is 15% when using @objc annotation (for this case)
It is worth to mention that single method call in the worst case (including the calculation) took 0.06 s / 1.000.000 = 0.00000006 sec what may seem really very small number, but in truth it depends what kind of an application you are writing, for what purpose methods are used and how many times method is invoked. For example:
- if you write a init method for setting up UI it doesn't matter so much matter how it is invoked
- if you are writing a method which is calculating over and over again in real time (navigation apps, games, ...) you will achieve performance boost
In the middle of the article I have mentioned swift compiler optimises method dispatch where possible. You may ask yourself, can I help compiler to optimise code?!? The answer is YES and you may already doing it - by following best practices of coding and OOP design. For example: whenever you mark object variable private or method private compiler can optimise it with applying static dispatch. For concrete example read this Apple's post, it is very good.
Some of you might still think "yeah bla bla bla" I am developing only simple apps and this is worth shit. Well wrong again - example from different world: do you know what is the one of the keys of success in payments in mobile technology and internet? Micro-payments. A 50 cents for this, 99 cents for that,... that doesn't sound a lot to people - and that's why people are buying. Well similar thing is happening here, just the result is the opposite: the micro-payments are in this case un-optimised calls and the result may be slower application (there is numerous calls in whole app lifecycle). Furthermore don't forget that you are writing code for the device with limited energy sources. You really don't want your app at the top of the iPhone's battery consumption apps :) ... and even if you are writing simple apps, remember: once you get used to "bad coding style" and ignore this little things, you may get into the trouble, when you will start something "serious".
And for the end: big thanks to leader of Slovenian iOS developers group Goran Bla?i? for brief explanation what is happening behind the scenes and what to look for on Google. The source code of the benchmark is here. The image in header of the image is taken from Apple's website.