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.

Sign up to discover human stories that deepen your understanding of the world.

--

--

Varun Tomar
Varun Tomar

Responses (2)

Write a response