To the depth of @objc and dynamic in Swift
You must have come across @objc
and dynamic
keyword while coding in Swift. The compiler gives an error if you are not using @objc
when it is needed. A developer may have a list of questions in mind :
- Where these keywords used?
- Why use
@objc
in a project which is purely in Swift? As it seems@objc
is something related to Objective-C - How methods behave with and without
@objc
behind the scene. - Exact purpose of
dynamic
? - Does
dynamic
also affects the time of execution of a program?
We are going to cover above in detail using a proper example.
“@objc”
Swift generates code that is only readable to other Swift code. But if we need to interact with the Objective-C runtime, we need to instruct Swift what to do. Here comes@objc
to make swift code available to Objective-C. Have you ever wonder why to prefix @objc
even if the code is written purely in Swift? The answer is, although you may have whole code in Swift there are some frameworks that use Objective-C runtime — UIKit is one of them. Simply we can say @objc
means we want our Swift code (class, method, property, etc.) to be visible from Objective-C.”
You must have noticed that we need to add the prefix @objc
for #selector(function) in case of adding target to Button, UITabGestureRecogniser or Timer. This is because the Selector is a C string that represents the name of the method at the runtime. For C string we have to invoke Objective-C runtime.
“dynamic”
Use of “dynamic” in Swift code requires when we want to use Objective-C dynamic dispatch. We may need this for KVO support or if we are doing method swizzling.
I am not going in detail of Method Swizzling as this is not in scope of this article. In simple words: Method Swizzling is the ability that Objective-C runtime gives us, to switch the implementation of an existing selector at runtime.
Note : As dynamic is Objective-C feature, we have to use @objc
also when we use dynamic
keyword.
I know it, buddy, where is the practicality?
I understand you must be excited to know how these things work behind the scene. Let's frame an example having a normal function, a function with the prefix @objc
and a function with the prefix dynamic
& @objc
. Please refer to the below Bird
class:
Next, we are going to see how SIL generated code looks for the above class. Use the command xcrun swiftc -emit-silgen <filename>.swift | xcrun swift-demangle
(to know more about SIL, please read this).
SIL generated for above Bird class is huge, I am going to show partially to make it understand:
I have explained how to read SIL in another article. For now, we just pick few as below :
%2
this takes a string literal raw pointer.%3
this is int literal for word length.%6
is the function reference.%7
apply%6
with parameters%2, %3, %4 and %5
, which are the literal rawpointer, word, Int and the type.return
the newly createdString
.
Now, what about other function with “@objc” prefix ? 🤔. SIL generated below👇
I have a few different findings for favouriteBirdObjc() :
- We have two different flavours of favouriteBirdObjc(), one for Swift and one for Objective-C.
- There is new a candidate “Thunk”, in case of Objective-C version of favouriteBirdObjc()
- There is a bridge at last.
Thunk : In Swift to Objective-C world, this is an additional method callable from Objective-C. It’s a thin wrapper, needs to do a call through to the native Swift method.
NOTE: I have same finding for favouriteBirdObjcAndDynamic() with one difference. If you scroll down to SIL generated code, you will come across Sil_vtable for the class. Have a look below, we have an entry for both favouriteBirdObjc() & favouriteBirdObjc() but no entry for favouriteBirdObjcAndDynamic() 👇
Yes dynamic
methods don’t appear in the vtable. Oh, I see🙃, but hold on! How it’s get called? 🤔🤔. To go under the hood, let’s make a call to these functions👇
let birdObj = Bird()
_ = birdObj.favoriteBird()
_ = birdObj.favoriteBirdObjc()
_ = birdObj.favoriteBirdObjcAndDynamic()
Note the addition of .foreign
keyword means it does not belong to the native Swift world. The calling convention has also changed from a plain old “method” to $@convention(objc_method)
.
One more interesting 🧐 fact I want to share is comparing the time of execution of these functions.
Refer above results we can say that @objc
doesn’t have much effect, but dynamic
definitely has a cost!
Thanks for reading this🙏🙏. Any feedback in the comment section would be appreciated. If you enjoyed reading this post, please share and give some claps.
You can follow me for fresh articles.