Few days ago, while I was scrolling through my Twitter feed, I stumble upon a simple Swift question. Surprisingly, out of 1334 people that responded, only 44% of people able to answer it correctly. The question goes like this.
var language = "Objc"
let code = { [language] in
print(language)
}
language = "Swift"
let newCode = code
newCode()
// What's the output?
// 1. Objc
// 2. Swift
// 3. Compiler Error
You can take a moment to think about it. Once you have figured it out, scroll down to checkout the answer.
The answer is “Objc“.
Is that the answer you choose? Great job if you are able to answer it correctly. If you choose the wrong answer, read on to find out why the answer is “Objc”.
Capturing Values in Closures
The core concept that required in order to get the correct answer for the above question is “capturing values in closures“.
In Swift, closures are basically a self-contained blocks of functionality that allow you to pass it around and use it anywhere in your code, and values capturing are one of the common behavior of closure. According to Swift documentation:
Closures can capture and store references to any constants and variables from the context in which they are defined.
https://docs.swift.org/
For the above question, the closure capture the value of language
by using the square bracket syntax, [language]
.
Since the value of language
have been captured before changing its value to “Swift”, thus calling newCode()
will print out “Objc”.
We can further verify this by removing the [language]
part of the code. You will find out that without capturing the value of language
, the output now become “Swift”.
Up until this point, you might think that you have fully understand why the answer is “Objc”. However, there is one more concept that we need to know in order for us to fully justify that the answer is “Objc” — String in Swift is a value type.
Value Types vs Reference Types
In Swift, the most basic distinguishing feature between value types and reference types is that, value types will keep a unique copy of its data for each of its instance.
On the other hand, reference types will share a single copy of data for each of its instance.
Since language
‘s data type is string, when the closure capture its value, an instance of language
will be created within the context of the closure. This means that the language
instance inside the closure and outside the closure are two different instances that contain their own unique value.
To verify that our understanding is correct, we can try to modify the code snippet by introducing a Language
class to replace the String
data type of language
.
// Define a Language class
class Language {
var name: String?
}
// Initialize a Language object
var language = Language()
language.name = "Objc"
let code = { [language] in
print(language.name!)
}
language.name = "Swift"
let newCode = code
newCode() // Output: "Swift"
Because classes in Swift is a reference type, the output for the above code snippet will be “Swift”, here’s why:
Conclusion
When capturing a value type instance in a closure, a new instance of that value type will be created within the context of the closure — This is the ultimate explanation for this simple Swift question.
In case you are interested, here’s the original tweet for this Swift question.
I hope you learn a thing or two from this simple yet interesting Swift question. I would like to end my article with this amazing quote from Lloyd Alexander:
We learn more by looking for the answer to a question and not finding it than we do from learning the answer itself.
Lloyd Alexander
If you like this article, checkout the following article that showcase another piece of interesting Swift code.
🔗 46% of People Think This Swift Code Has Error, Apparently, It Doesn’t!
Further Reading
If you like this article, feel free to share it with your friends and leave me a comment. You can follow me on Twitter for more articles related to iOS development.
Thanks for reading! 👨🏼💻
👋🏻 Hey!
While you’re still here, why not check out some of my favorite Mac tools on Setapp? They will definitely help improve your day-to-day productivity. Additionally, doing so will also help support my work.
- ✨ Bartender: Superpower your menu bar and take full control over your menu bar items.
- ✨ CleanShot X: The best screen capture app I’ve ever used.
- ✨ PixelSnap: Measure on-screen elements with ease and precision.
- ✨ iStat Menus: Track CPU, GPU, sensors, and more, all in one convenient tool.