To the depth of @objc and dynamic in Swift

Varun Tomar
5 min readMay 29, 2020

--

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 :

  1. Where these keywords used?
  2. Why use @objc in a project which is purely in Swift? As it seems @objc is something related to Objective-C
  3. How methods behave with and without @objc behind the scene.
  4. Exact purpose of dynamic?
  5. Does dynamic also affects the time of execution of a program?

We are going to cover above in detail using a proper example.

Photo by Aleksander Vlad on Unsplash

“@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:

SIL for favoriteBird()

I have explained how to read SIL in another article. For now, we just pick few as below :

  • %2this takes a string literal raw pointer.
  • %3this 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 created String.

Now, what about other function with “@objc” prefix ? 🤔. SIL generated below👇

SIL for favoriteBirdObjc()

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()
SIL for above function calling code

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.

Time taken by normal favoriteBird()
Time taken by normal favoriteBirdObjc()

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.

--

--

Varun Tomar
Varun Tomar

Responses (2)