Swift Interview Questions and Answers
Practice Swift interview questions and answers organized by difficulty, with revealable explanations and related topics.
Junior questions
6 questions- Question 1Junior
What are Extensions used for in Swift?
- Swift
Reveal answerHide answer
Extensions add new functionality to an existing class, structure, enumeration, or protocol type. This includes the ability to extend types for which you do not have access to the original source code. Extensions are similar to categories in Objective-C Extensions in Swift can: Add computed instance properties and computed type properties Define instance methods and type methods Provide new initializers Define subscripts Define and use new nested types Make an existing type conform to a protocol
- Question 2Junior
What are Extensions used for in Swift?
- Swift
Reveal answerHide answer
Extensions add new functionality to an existing class, structure, enumeration, or protocol type. This includes the ability to extend types for which you do not have access to the original source code. Extensions are similar to categories in Objective-C Extensions in Swift can: Add computed instance properties and computed type properties Define instance methods and type methods Provide new initializers Define subscripts Define and use new nested types Make an existing type conform to a protocol
- Question 3Junior
What is a Serial Queue?
- Swift
Reveal answerHide answer
A Serial Queue allows us to perform only one task at a time, no matter the way of execution, i.e. Synchronous or Asynchronous . In short, the code executed by Serial DispatchQueue is not parallel and has to wait for the first task to complete. This way of execution is also known as First In, First Out (FIFO). All the queues need to wait for the completion of the previous queue. By default, DispatchQueue is a serial queue. Consider:
let queue = DispatchQueue(label: "com.swiftpal.dispatch.serial") queue.async() { Thread.sleep(forTimeInterval: 3) // Wait for 3 seconds print("Task 1 Done") } queue.async() { Thread.sleep(forTimeInterval: 1) // Wait for 1 second. print("Task 2 Done") }/* Output: Task 1 Done Task 2 Done */ - Question 4Junior
What is the difference between Upcast and Downcast in Swift?
- Swift
Reveal answerHide answer
The upcast, going from a derived class to a base class, can be checked at compile time and will never fail. However, downcasts can fail since you can’t always be sure about the specific class. If you have a UIView, it’s possible it’s a UITableView or maybe a UIButton. downcasts must be either optional with as? or “forced failable” with as! when sure about the type
- Question 5Junior
What’s the difference between == and === ?
- Swift
Reveal answerHide answer
== operator checks if the values are the same, comparing value types. "equal to" === operator checks if the references point the same instance (both point to the same memory address), comparing reference types. "identical to"
- Question 6Junior
How to iterate a loop with index and element in Swift?
- Swift
Reveal answerHide answer
Swift 5 provides a method called enumerated() for Array :
let list = ["Car", "Bike", "Plane", "Boat"] for (index, element) in list.enumerated() { print(index, ":", element) }/* prints: 0 : Car 1 : Bike 2 : Plane 3 : Boat */
Mid questions
28 questions- Question 1Mid
What's the main difference between the Array , Set and Dictionary collection type?
- Swift
Reveal answerHide answer
Arrays are effectively ordered lists and are used to store lists of information of the same type in cases where order is important. The same value can appear in an array multiple times at different positions. Sets are different in the sense that order does not matter and these will be used in cases where order does not matter. Dictionaries are used to store key, value pairs and are used when you want to easily find a value using a key, just like in a dictionary. Unlike items in an array, items in a dictionary do not have a specified order.
- Question 2Mid
Rewrite this code in a Swift way
- Swift
Reveal answerHide answer
Problem
Consider:
let list = [Int](1...5) var arrayOfTuples = [(Int, Int)]() for (index, element) in list.enumerated() { arrayOfTuples += [(index, element)] } print(arrayOfTuples) // prints [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]Can you rewrite this code in a more "swiftier" way?
Solution
Yeap, the way to do it is:
let list = [Int](1...5) let arrayOfTuples = Array(list.enumerated()) print(arrayOfTuples) // prints [(offset: 0, element: 1), (offset: 1, element: 2), (offset: 2, element: 3), (offset: 3, element: 4), (offset: 4, element: 5)]or with map :
let list = [Int](1...5) let arrayOfDictionaries = list.enumerated().map { (a, b) in return [a : b] } print(arrayOfDictionaries) // prints [[0: 1], [1: 2], [2: 3], [3: 4], [4: 5]] - Question 2Mid
Explain the defer usage in Swift
- Swift
Reveal answerHide answer
A defer statement is used for executing code just before transferring program control outside of the scope that the statement appears in.
func updateImage() { defer { print("Did update image") } print("Will update image") imageView.image = updatedImage }// Will update Image // Did update imageThe most common use case seen around is opening and closing a context within a scope, for example when handling access to files. A FileHandle requires to be closed once the access has been finished. You can benefit from the defer statement to ensure you don’t forget to do this.
func writeFile() { let file: FileHandle? = FileHandle(forReadingAtPath: filepath) defer { file?.closeFile() }// Write changes to the file } - Question 3Mid
How to sort array of custom objects by property value in Swift?
- Swift
Reveal answerHide answer
For in-place sorting consider:
var images : [imageFile] = [] images.sorted(by: { $0.fileID > $1.fileID }) // swift 3+ images.sorted { $0.fileID < $1.fileID } // exploiting a trailing closureIf you are going to do this repeatedly and prefer to define a function, one way is: func sorterForFileIDASC(this:imageFile, that:imageFile) -> Bool { return this.fileID > that.fileID
} ... images.sort(by: sorterForFileIDASC) - Question 3Mid
Explain the difference between DispatchQueue.main.async and DispatchQueue.main.sync ?
- Swift
Reveal answerHide answer
When you use async it lets the calling queue move on without waiting until the dispatched block is executed. On the contrary sync will make the calling queue stop and wait until the work you've dispatched in the block is done. Therefore sync is subject to lead to deadlocks. Try running DispatchQueue.main.sync from the main queue and the app will freeze because the calling queue will wait until the dispatched block is over but it won't be even able to start (because the queue is stopped and waiting). When to use sync ? When you need to wait for something done on a DIFFERENT queue and only then continue working on your current queue Example of using sync: On a serial queue you could use sync as a mutex in order to make sure that only one thread is able to perform the protected piece of code at the same time.
- Question 4Mid
When should I use deinit ?
- Swift
Reveal answerHide answer
It's not required that you implement that method, but you can use it if you need to do some action or cleanup before deallocating the object. Consider:
struct Bank { static var coinsInBank = 10_000 static func vendCoins(var numberOfCoinsToVend: Int) -> Int { numberOfCoinsToVend = min(numberOfCoinsToVend, coinsInBank) coinsInBank -= numberOfCoinsToVendreturn numberOfCoinsToVend
} static func receiveCoins(coins: Int) { coinsInBank += coins } } class Player { var coinsInPurse: Int init(coins: Int) { coinsInPurse = Bank.vendCoins(coins) } func winCoins(coins: Int) { coinsInPurse += Bank.vendCoins(coins) } deinit { Bank.receiveCoins(coinsInPurse) } }So whenever the player is removed from the game, its coins are returned to the bank.
- Question 4Mid
How to append one array to another array in Swift?
- Swift
Reveal answerHide answer
Problem
Consider:
var first = ["John", "Paul"] let second = ["George", "Ringo"]How to append one array to another array in Swift? Note: the first array mutable so we can join the second array to it.
Solution
One option for joining arrays is the append(contentsOf:) method, used like this:
first.append(contentsOf: second)Another option is using the += operator, which is overloaded for arrays to combine elements:
first += secondThe third option is to use a regular + to create a new array by combining two others:
let third = first + secondAll three options produce the same resulting array.
- Question 5Mid
What’s a strong reference, and why do we need it?
- Swift
Reveal answerHide answer
Strong references increase the retain count of instances they reference (by 1). This prevents the Automatic Reference Counting (ARC) from removing the object from memory, as long as the object is in use. In essence,
**as long as anything has a strong reference to an object, it will not be deallocated. **You can assert the following truths about strong references: In Swift, a strong reference is the default. When you create a variable, constant or property for a reference type, a strong reference is created by default. This is also true for passing references into functions or closures. An instance is only deallocated when its retain count is zero; when no other objects hold onto it. A strong reference increases the retain count by one, so the strong reference must be broken before the instance it references can be deallocated.
- Question 6Mid
When to use strong , weak and unowned references?
- Swift
Reveal answerHide answer
To determine if you even need to worry about strong, weak, or unowned, ask, “Am I dealing with reference types”. If you’re working with Structs or Enums, ARC isn’t managing the memory for those Types and you don’t even need to worry about specifying weak or unowned for those constants or variables. Strong references are fine in hierarchical relationships where the parent references the child, but not vice- versa. In fact, strong references are the most appropraite kind of reference most of the time. When two instances are optionally related to one another, make sure that one of those instances holds a weak reference to the other. When two instances are related in such a way that one of the instances can’t exist without the other, the instance with the mandatory dependency needs to hold an unowned reference to the other instance.
- Question 6Mid
When to use a set rather than an array in Swift?
- Swift
Reveal answerHide answer
You should use a set rather than an array if all the following criteria are true:
- You intend to add each item only once. Sets never allow duplicates.
- You don’t care about the order of the items in the set.
- You don’t need to use APIs that require arrays.
- You’re storing Hashable types, either your own or one of Swift’s built-in types likes strings and integers. Sets use hash values for fast look up of items.
- Question 7Mid
What’s a strong reference, and why do we need it?
- Swift
Reveal answerHide answer
Strong references increase the retain count of instances they reference (by 1). This prevents the Automatic Reference Counting (ARC) from removing the object from memory, as long as the object is in use. In essence,
**as long as anything has a strong reference to an object, it will not be deallocated. **You can assert the following truths about strong references: In Swift, a strong reference is the default. When you create a variable, constant or property for a reference type, a strong reference is created by default. This is also true for passing references into functions or closures. An instance is only deallocated when its retain count is zero; when no other objects hold onto it. A strong reference increases the retain count by one, so the strong reference must be broken before the instance it references can be deallocated.
- Question 7Mid
Explain the defer usage in Swift
- Swift
Reveal answerHide answer
A defer statement is used for executing code just before transferring program control outside of the scope that the statement appears in.
func updateImage() { defer { print("Did update image") } print("Will update image") imageView.image = updatedImage }// Will update Image // Did update imageThe most common use case seen around is opening and closing a context within a scope, for example when handling access to files. A FileHandle requires to be closed once the access has been finished. You can benefit from the defer statement to ensure you don’t forget to do this.
func writeFile() { let file: FileHandle? = FileHandle(forReadingAtPath: filepath) defer { file?.closeFile() }// Write changes to the file } - Question 8Mid
When to use @objc attribute?
- Swift
Reveal answerHide answer
By default Swift generates code that is only available to other Swift code, but if you need to interact with the Objective-C runtime – all of UIKit, for example – you need to tell Swift what to do. That’s where the @objc attribute comes in: when you apply it to a class or method it instructs Swift to make those things available to Objective-C as well as Swift code. So, any time you want to call a method from a UIBarButtonItem or a Timer , you’ll need to mark that method using @objc so it’s exposed – both of those, and many others, are Objective-C code. Consider:
class MyController: UIViewController { @objc func authenticateUser() { } } - Question 9Mid
What is trailing closure syntax?
- Swift
Reveal answerHide answer
Many functions in iOS accept multiple parameters where the final parameter is a closure. Trailing closure syntax is a little piece of syntactic sugar that makes particularly common code more pleasant to read and write. Consider:
func greetThenRunClosure(name: String, closure: () -> ()) { print("Hello, \(name)!") closure() } Use: greetThenRunClosure(name: "Paul") { print("The closure was run") }This functionality is available wherever a closure is the final parameter to a function.
- Question 9Mid
Why and how to use associated values in enums?
- Swift
Reveal answerHide answer
Enums with associated values let you associate extra data with an enum case. This helps make them significantly more useful, because we can create gradations of cases rather than have them be absolute. Consider:
enum Weather { case sunny case cloudy(coverage: Int) case windy(speed: Int) case rainy(chance: Int) } let london = Weather.cloudy(coverage: 90) let gusty = Weather.windy(speed: 10) let guaranteedRain = Weather.rainy(chance: 100) - Question 10Mid
What is typecasting?
- Swift
Reveal answerHide answer
Sometimes you know information that Swift doesn’t: you know that the UIViewController you have a reference to is in fact your custom PictureViewController subclass, and you want to treat it like one. As an example, here’s how we create new view controllers from a storyboard:
let vc = storyboard?.instantiateViewController(withIdentifier: "Detail") as? DetailViewControllerThat as? is a typecast - we’re passing on to Swift the knowledge that the view controller we expect to be created is in fact a DetailViewController . If the typecast fails then vc will be nil, but if it succeeds then Swift will be able to use it as a DetailViewController .
- Question 10Mid
What’s the difference between a protocol and a class in Swift?
- Swift
Reveal answerHide answer
In their basic form, a protocol describes what an unknown type of object can do. You might say it has two or three properties of various types, plus methods. But that protocol never includes anything inside the methods, or provides actual storage for the properties. In a more advanced form, you can write extensions on your protocols that provide default implementations of the methods. You still can’t provide storage for properties, however. In comparison, classes are concrete things. While they might adopt protocols – i.e., say they implement the required properties and methods – they aren’t required to do that. You can create objects from classes, whereas protocols are just type definitions. Try to think of protocols as being abstract definitions, whereas classes and structs are real things you can create.
- Question 11Mid
When to use a set rather than an array in Swift?
- Swift
Reveal answerHide answer
You should use a set rather than an array if all the following criteria are true:
- You intend to add each item only once. Sets never allow duplicates.
- You don’t care about the order of the items in the set.
- You don’t need to use APIs that require arrays.
- You’re storing Hashable types, either your own or one of Swift’s built-in types likes strings and integers. Sets use hash values for fast look up of items.
- Question 11Mid
What does the Swift mutating keyword mean?
- Swift
Reveal answerHide answer
Being the value type structs are immutable. Meaning other variables can not change the values for instance of structure at any given point. The mutating word is required for changing the values of self variables inside structure's function only. For. e.g
struct MyStruct { var abc: String = "initila value" func changeValue() { abc = "some other value". //Compile time error: Cannot assign to property: 'self' is immutable.Mark method 'mutating' to make 'self' mutable.
} }Here as we are trying to change value of variable abc inside function declared in struct itself, we get the compile time error. So here we need to make function mutating to make change value inside structure. Hence the correct code will be:
struct MyStruct { var abc: String = "initila value" mutating func changeValue() { abc = "some other value" } } - Question 12Mid
What's the difference between Self vs self ?
- Swift
Reveal answerHide answer
When you’re writing protocols and protocol extensions, there’s a difference between Self (capital S) and self (lowercase S). When used with a capital S, Self refers to the type that conform to the protocol, e.g. String or Int . When used with a lowercase S, self refers to the value inside that type, e.g. “hello” or 556. As an example, consider this extension on BinaryInteger :
extension BinaryInteger { func squared() -> Self {return self * self
} }Remember, Self with a capital S refers to whatever type is conforming to the protocol. In the example above, Int conforms to BinaryInteger , so when called on Int the method returns an Int . On the other hand, self with a lowercase S refers to whatever value the type holds. If the example above were called on an Int storing the value 5 it would effectively be 5 * 5 .
- Question 12Mid
How to sort array of custom objects by property value in Swift?
- Swift
Reveal answerHide answer
For in-place sorting consider:
var images : [imageFile] = [] images.sorted(by: { $0.fileID > $1.fileID }) // swift 3+ images.sorted { $0.fileID < $1.fileID } // exploiting a trailing closureIf you are going to do this repeatedly and prefer to define a function, one way is: func sorterForFileIDASC(this:imageFile, that:imageFile) -> Bool { return this.fileID > that.fileID
} ... images.sort(by: sorterForFileIDASC) - Question 13Mid
What does the required keyword in Swift mean?
- Swift
Reveal answerHide answer
The required keyword means that inheriting classes must provide an implementation of the method. You do not have to provide an explicit implementation of a required initializer if you can satisfy the requirement with an inherited initializer. Whenever your class requires a certain setup when subclassed, mark the initializer as required . This makes sure that any new subclass instances will add support for that kind of usage.
- Question 14Mid
What Classes and Structs have in common in Swift and what are their differences?
- Swift
Reveal answerHide answer
The features classes and structs have in common: Both structs and classes can define properties to store values, and they can define functions They can define subscripts to provide access to values with subscript syntax They can define initializers to set up their initial state, with init() They can be extended with extension (this is important!) They can conform to protocols, for example to support Protocol Oriented Programming They can work with generics to provide flexible and reusable types Classes support a few more capabilities that structs don’t have: Classes can inherit from another class, like you inherit from UIViewController to create your own view controller subclass Classes can be deinitialized, i.e. you can invoke a deinit() function before the class is destroyed Classes are reference types and structs are value types Value Type: When you copy a value type (i.e., when it’s assigned, initialized or passed into a function), each instance keeps a unique copy of the data. If you change one instance, the other doesn’t change too. Reference Type: When you copy a reference type, each instance shares the data. The reference itself is copied, but not the data it references. When you change one, the other changes too.
- Question 14Mid
What are the differences between functions and methods in Swift?
- Swift
Reveal answerHide answer
Functions are self-contained chunks of code that perform a specific task. You give a function a name that identifies what it does, and this name is used to “call” the function to perform its task when needed. With functions, there is no self. Methods are functions that are associated with a particular type. Classes, structures, and enumerations can all define instance methods, which encapsulate specific tasks and functionality for working with an instance of a given type. The method can access self .
func someFunc{ //some code } class someClass{ func someMethod{ //some code } } - Question 15Mid
When would you use self in a method?
- Swift
Reveal answerHide answer
By far the most common reason for using self is inside an initializer, where your likely to want parameter names that match the property names of your type, like this:
struct Student { var name: String var bestFriend: String init(name: String, bestFriend: String) { print("Enrolling \(name) in class…") self.name = name self.bestFriend = bestFriend } } - Question 17Mid
What is difference between as? , as! and as in Swift?
- Swift
Reveal answerHide answer
In Swift 1.2 and later, as can only be used for
upcasting (or disambiguation) and pattern matching:// 'as' for pattern matching switch item { case let obj as MyObject: // this code will be executed if item is of type MyObject case let other as SomethingElse: // this code will be executed if item is of type SomethingElse ... }as? produces an optional value, which is either the value if it can be cast to the specified type, or nil if it can't. as! doesn't produce an optional, it just produces a value of the specified type, and if the cast fails, it aborts the program. Saying foo as! SomeType is basically the same thing as saying (foo as? SomeType)! (except you get a better error message). You should only ever use as! if you're 100% certain the cast will succeed (because if you're wrong, the whole program aborts).
- Question 19Mid
What is a good use case for an inout parameter?
- Swift
Reveal answerHide answer
Function parameters are constants by default. Trying to change the value of a function parameter from within the body of that function results in a compile-time error. inout means that modifying the local variable will also modify the passed-in parameters. Without it, the passed- in parameters will remain the same value. Trying to think of reference type when you are using inout and value type without using it.
- A good use case will be swap function that it will modify the passed-in parameters.
- Consider removing the overhead of copying as well. If you have a function that takes a somewhat memory-wise large value type as argument (say, a large structure type) and that returns the same type, and finally where the function return is always used just to replace the caller argument, then inout is to prefer as associated function parameter.
struct MyStruct { private var myInt: Int = 1// ... lots and lots of stored properties mutating func increaseMyInt() { myInt += 1 } } /* call to function _copies_ argument to function property 'myHugeStruct' (copy 1) function property is mutated function returns a copy of mutated property to caller (copy 2) */ func myFunc(var myHugeStruct: MyStruct) -> MyStruct { myHugeStruct.increaseMyInt()return myHugeStruct }/* call-by-reference, no value copy overhead due to inout opimization */
func myFuncWithLessCopyOverhead(inout myHugeStruct: MyStruct) { myHugeStruct.increaseMyInt() } var a = MyStruct() a = myFunc(a) // copy, copy: overhead myFuncWithLessCopyOverhead(&a) // call by reference: no memory reallocation - Question 20Mid
In Swift enumerations, what’s the difference between raw values and associated values?
- Swift
Reveal answerHide answer
Raw values are used to associate constant (literal) values to enum cases. The value type is part of the enum type, and each enum case must specify a unique raw value (duplicate values are not allowed). The following example shows an enum with raw values of type Int:
enum IntEnum : Int { case ONE = 1 case TWO = 2 case THREE = 3 }An enum value can be converted to its raw value by using the rawValue property:
var enumVar: IntEnum = IntEnum.TWO var rawValue: Int = enumVar.rawValueA raw value can be converted to an enum instance by using a dedicated initializer:
var enumVar: IntEnum? = IntEnum(rawValue: 1)Associated values are used to associate arbitrary data to a specific enum case. Each enum case can have zero or more associated values, declared as a tuple in the case definition:
enum AssociatedEnum { case EMPTY case WITH_INT(value: Int) case WITH_TUPLE(value: Int, text: String, data: [Float]) } Whereas the type(s) associated to a case are part of the enum declaration, the associated value(s) are instancespecific, meaning that an enum case can have different associated values for different enum instances.
Senior questions
25 questions- Question 1Senior
When to use fileprivate access modifier in Swift?
- Swift
Reveal answerHide answer
fileprivate keyword restricts the use of an entity to its defining source file. You typically use fileprivate access to hide the implementation details of a specific piece of functionality when those details are used within an entire file.
// First.framework – A.swift internal struct A { fileprivate static let x: Int } A.x // ok // First.framework – B.swift A.x // error: x is not available - Question 2Senior
What is Copy on Write (Cow) in Swift?
- Swift
Reveal answerHide answer
Copy on Write (CoW or COW), sometimes referred to as implicit sharing or shadowing is a resource- management technique used in computer programming to efficiently implement a “duplicate” or “copy” operation on modifiable resources. If a resource duplicated but not modified it is not necessary to create a new resource. The resource can be shared between the copy and the original. At a basic level, Array is just a structure that holds a reference to a heap-allocated buffer containing the elements – therefore multiple Array instances can reference the same buffer. When you come to mutate a given array instance, the implementation will check if the buffer is uniquely referenced, and if so, mutate it directly. Otherwise, the array will perform a copy of the underlying buffer in order to preserve value semantics. Copy on write is a feature specifically added to Swift arrays and dictionaries; you don't get it for free in your own data types.
- Question 3Senior
When is it safe to use an unowned reference?
- Swift
Reveal answerHide answer
An unowned reference is assumed to always have a value. So you should only use it when you are sure that the reference will never be nil . Suppose we have two classes Customer and CreditCard . A customer can exist without a credit card, but a credit card will not exist without a customer, i.e. it can be assumed that a credit card will always have a customer. So, they should have the following relationship:
class Customer { var card: CreditCard? } class CreditCard { unowned let customer: Customer } - Question 4Senior
Explain the difference between weak and unowned references. Provide an example.
- Swift
Reveal answerHide answer
Both weak and unowned references do not create a strong hold on the referred object (a.k.a. they don't increase the retain count in order to prevent ARC from deallocating the referred object). A weak reference allows the possibility of it to become nil (this happens automatically when the referenced object is deallocated), therefore the type of your property must be optional - so you, as a programmer, are obligated to check it before you use it (basically the compiler forces you, as much as it can, to write safe code). Used for scenarios when _both entities can exis_t without having a strict dependency upon the other.
class Person { let name: String init(name: String) { self.name = name } var apartment: Apartment? } class Apartment { let number: Int init(number: Int) { self.number = number } weak var tenant: Person? }or
Person ===(strong)==> Apartment Person <==(weak)===== ApartmentThe Person and Apartment example shows a situation where two properties, both of which are allowed to be nil , have the potential to cause a strong reference cycle and can exist without having a strict dependency upon the other. An unowned reference presumes that it will never become nil during its lifetime. An unowned reference must be set during initialization - this means that the reference will be defined as a non-optional type that can be used safely without checks. If somehow the object being referred to is deallocated, then the app will crash when the unowned reference is used.
class Customer { let name: String var card: CreditCard? init(name: String) { self.name = name } } class CreditCard { let number: UInt64 unowned let customer: Customer init(number: UInt64, customer: Customer) { self.number = number; self.customer = customer } }In this example, a Customer may or may not have a CreditCard , but a CreditCard will always be associated with a Customer .
- Question 5Senior
When should you use Classes over Structs?
- Swift
Reveal answerHide answer
Classes have a few extra characteristics that structs don’t have: Classes can inherit from another class, which you can’t do with structs. With classes, you can write class MyViewController : UIViewController to create a subclass of UIViewController . Structs can implement protocols, can be extended, and can work with generics, though but with classes, you can clearly define a parent-child connection between subclass and superclass. Classes can be deinitialized, i.e. they can implement a deinit function. Also, you can make one or more references to the same class (i.e., classes are a reference type). Classes come with the built-in notion of identity, because they’re reference types. With the identity operator === you can check if two references (variables, constants, properties, etc.) refer to the same object. It’s important to note here that you need to use classes if you want to interop between Swift and Objective-C.
- Question 6Senior
When should you use Structs over Classes?
- Swift
Reveal answerHide answer
It’s recommended to use struct in Swift by default. Structs are helpful in these scenarios, too: Simple Data Types Use structs for simple data types. Think about database objects you want to pass around in your code, like NewsItem , Task or User . Since they’re so well-defined, and often don’t need to accommodate complex relationships between objects, it’s simpler to use structs. Thread Safety In a multi-threaded environment, for instance with a database connection that’s opened in a different thread, structs are safer. They can be copied from one thread to another thread, without running the risk of a race condition or deadlock. Classes do not have this inherent safety, unless they’re deliberately made thread-safe. Mostly Structs Scenario When the properties of a struct are mostly value types too, like String , it makes sense to wrap them in a struct instead of a class. It’s OK to use structs within class types, but pay extra attention if you use classes within struct types. Classes are reference types, so if you’re unaware that your struct references a shared class instance, and your struct gets copied, both structs share a reference to that class! Don’t Need Inheritance Structs cannot inherit code from other structs. If you don’t need inheritance, it’s smarter to use structs
(instead of classes). Structs can still conform to protocols, and they can be extended, so you still have a few options for creating flexible, composable code. Using structs has a huge benefit: it’s easier to reason about data changes in your code. When a type is a struct, you can be certain that no other part of your code can hold on to a reference to that struct.
- Question 8Senior
Is there a way to create an abstract class in Swift?
- Swift
Reveal answerHide answer
There are no abstract classes in Swift. You can achieve the same behaviour as abstract class with protocols and protocol extensions. First, you write a protocol that acts as an interface for all the methods that have to be implemented in all types that conform to it.
protocol Drivable { var speed: Float { get set } }Then you can add default behaviour to all types that conform to it
extension Drivable { func accelerate(by: Float) { speed += by } }You can now create new types by implementing Drivable .
struct Car: Drivable { var speed: Float = 0.0 init() {} } let c = Car() c.accelerate(10)So basically you get:
-
Compile time checks that guarantee that all Drivable s implement speed
-
You can implement default-behaviour for all types that conform to Drivable ( accelerate )
-
Drivable is guaranteed not to be instantiated since it's just a protocol
- Question 8Senior
Explain when to use different Swift casting operators?
- Swift
Reveal answerHide answer
The as operator The as operator performs a cast when it is known at compile time that the cast always succeeds, such as upcasting or bridging. Upcasting lets you use an expression as an instance of its type’s supertype, without using an intermediate variable. This is the most preferable operator to use, when possible. It guarentees success, without worrying about unwrapping an optional or risking a crash. The as? operator The as? operator performs a conditional cast of the expression to the specified type. The as? operator returns an optional of the specified type. At runtime, if the cast succeeds, the value of expression is wrapped in an optional and returned; otherwise, the value returned is nil . If casting to the specified type is guaranteed to fail or is guaranteed to succeed, a compile-time error is raised. This is the second most preferable operator to use. Use it to safely handle the case in which a casting operator can't be performed.
The as! operator The as! operator performs a forced cast of the expression to the specified type. The as! operator returns a value of the specified type, not an optional type. If the cast fails, a runtime error is raised. The behavior of x as! T is the same as the behavior of (x as? T)! . This is the least preferable operator to use. I strongly advise against abusing it. Attempting to cast an expression to an incompatible type crashes your program.
- Question 9Senior
How to add a custom initializer to a struct without losing its memberwise initializer?
- Swift
Reveal answerHide answer
All structs in Swift come with a default memberwise initializer, which is an initializer that accepts values for all the properties in the struct. However, as soon as you add your own initializer to the struct that memberwise initializer goes away, because it’s possible you’re doing special work that the default initializer isn’t aware of. If you want to keep both the default initializer and your own custom ones, there’s a simple trick: create your initializers inside an extension rather than as part of the main struct definition. For example:
struct Person { var firstName: String var lastName: String } extension Person { init(name: String) { let split = name.components(separatedBy: " ") firstName = split.first ?? "" lastName = split.last ?? "" } }Because my custom initializer is inside an extension, you can create instances of Person in two ways:
let taylor1 = Person(firstName: "Taylor", lastName: "Swift") let taylor2 = Person(name: "Taylor Swift") - Question 10Senior
When should you use Structs over Classes?
- Swift
Reveal answerHide answer
It’s recommended to use struct in Swift by default. Structs are helpful in these scenarios, too: Simple Data Types Use structs for simple data types. Think about database objects you want to pass around in your code, like NewsItem , Task or User . Since they’re so well-defined, and often don’t need to accommodate complex relationships between objects, it’s simpler to use structs. Thread Safety In a multi-threaded environment, for instance with a database connection that’s opened in a different thread, structs are safer. They can be copied from one thread to another thread, without running the risk of a race condition or deadlock. Classes do not have this inherent safety, unless they’re deliberately made thread-safe. Mostly Structs Scenario When the properties of a struct are mostly value types too, like String , it makes sense to wrap them in a
struct instead of a class. It’s OK to use structs within class types, but pay extra attention if you use classes within struct types. Classes are reference types, so if you’re unaware that your struct references a shared class instance, and your struct gets copied, both structs share a reference to that class! Don’t Need Inheritance Structs cannot inherit code from other structs. If you don’t need inheritance, it’s smarter to use structs (instead of classes). Structs can still conform to protocols, and they can be extended, so you still have a few options for creating flexible, composable code. Using structs has a huge benefit: it’s easier to reason about data changes in your code. When a type is a struct, you can be certain that no other part of your code can hold on to a reference to that struct.
- Question 10Senior
What to use: [String: Any] or [String: AnyObject] ?
- Swift
Reveal answerHide answer
Whether you use Any or AnyObject depends on your intended use: If your dictionary will be used only within Swift code, then you should use Any because your types ( Int , Double , Float , String , Array , and Dictionary ) are not objects. If you will be passing your dictionary to Objective-C routines that expect an NSDictionary , then you should use AnyObject . When you import Foundation or import UIKit or import Cocoa , it is possible to declare your array as [String: AnyObject] , but in this case Swift is treating your Int , Double , Float literals as NSNumber , your String s as NSString , your Array s as NSArray , and your dictionaries as NSDictionary , all of which are objects. A dictionary using AnyObject as the value type is convertible to NSDictionary , but one using Any is not.
- Question 11Senior
What’s the difference between Any and AnyObject ?
- Swift
Reveal answerHide answer
Swift provides two special types for working with nonspecific types: Any can represent an instance of any type at all (both value and reference types), including function types. AnyObject can represent an instance of any class type (reference types). Use Any and AnyObject only when you explicitly need the behavior and capabilities they provide. It is always better to be specific about the types you expect to work with in your code.
- Question 11Senior
Explain what will be the output of this code?
- Swift
Reveal answerHide answer
Problem
Consider:
func printStringNumbers() { defer { print("1") } defer { print("2") } defer { print("3") } print("4") }What will be the output of this code?
Solution
A defer statement is used for executing code just before transferring program control outside of the scope that the statement appears in. If multiple `defer statements appear in the same scope, the order they appear is the reverse of the order they are executed. 4, 3, 2, 1
- Question 12Senior
How to add a custom initializer to a struct without losing its memberwise initializer?
- Swift
Reveal answerHide answer
All structs in Swift come with a default memberwise initializer, which is an initializer that accepts values for all the properties in the struct. However, as soon as you add your own initializer to the struct that memberwise initializer goes away, because it’s possible you’re doing special work that the default initializer isn’t aware of. If you want to keep both the default initializer and your own custom ones, there’s a simple trick: create your initializers inside an extension rather than as part of the main struct definition. For example:
struct Person { var firstName: String var lastName: String } extension Person { init(name: String) { let split = name.components(separatedBy: " ") firstName = split.first ?? "" lastName = split.last ?? "" } }Because my custom initializer is inside an extension, you can create instances of Person in two ways:
let taylor1 = Person(firstName: "Taylor", lastName: "Swift") let taylor2 = Person(name: "Taylor Swift") - Question 13Senior
Is there a way to create an abstract class in Swift?
- Swift
Reveal answerHide answer
There are no abstract classes in Swift. You can achieve the same behaviour as abstract class with protocols and protocol extensions. First, you write a protocol that acts as an interface for all the methods that have to be implemented in all types that conform to it.
protocol Drivable { var speed: Float { get set } }Then you can add default behaviour to all types that conform to it
extension Drivable { func accelerate(by: Float) { speed += by } }You can now create new types by implementing Drivable .
struct Car: Drivable { var speed: Float = 0.0 init() {} } let c = Car() c.accelerate(10)So basically you get:
- Compile time checks that guarantee that all Drivable s implement speed
- You can implement default-behaviour for all types that conform to Drivable ( accelerate )
- Drivable is guaranteed not to be instantiated since it's just a protocol
- Question 14Senior
Are there any differences between Protocol in Swift vs Interface in Java?
- Java Swift
Reveal answerHide answer
Essentially protocols are very similar to Java interfaces except for: Swift protocols can also specify properties that must be implemented (i.e. fields) Swift protocols need to deal with value/reference through the use of the mutating keyword (because protocols can be implemented by structures, enumerations or classes). you can combine protocols at any point using "Protocol Composition". This replaces the older swift protocol<A, B> way of protocol composition. For example, declaring a function parameter that must adhere to protocol Named and Aged as: func wishHappyBirthday(to celebrator: Named & Aged) {}
- Question 15Senior
What’s the difference between a static variable and a class variable?
- Swift
Reveal answerHide answer
Both the static and class keywords allow us to attach variables to a class rather than to instances of a class. Where static and class differ is how they support inheritance: When you make a static property it becomes owned by the class and cannot be changed by subclasses, whereas when you use class it may be overridden if needed. Consider:
class Person { static var count: Int {return 250
} class var averageAge: Double {return 30
} } class Student: Person { // THIS ISN'T ALLOWED // override static var count: Int { // return 150 // } // THIS IS ALLOWED override class var averageAge: Double {return 19.5
} } - Question 16Senior
Provide an example of code with a memory leak
- Swift
Reveal answerHide answer
Consider:
class A { var b: B! deinit { print("deinit of A") } } class B { var a: A! deinit { print("deinit of B") } } do { let a = A() let b = B() a.b = b b.a = a } If you run this code, it will print nothing. It means that deinit (and deallocation) never called for both objectsand they just leaked. The A cannot be deallocated as long as the B is alive and the B cannot be deallocated as long as the A is alive. That situation is called Strong Reference Cycle and leads to memory leaks. But if you declare one of the properties as weak :
class A { weak var b: B! deinit { print("deinit of A") } }Then deinit will be called and you'll see messages in console.
- Question 16Senior
What is the difference between functions and closures?
- Swift
Reveal answerHide answer
Roughly, a closure is a block of code that may capture variable values from its surrounding scope.
let simpleClosure:(String) -> (String) = { name in let greeting = "Hello, World! " + "Program"return greeting
} let result = simpleClosure("Hello, World") print(result)Roughly, a function is a statically defined block of code that may use variable values from its surrounding scope. Note that these are almost exactly the same thing, at least at a conceptual level. The difference is that functions are traditionally invoked in the scope where they are defined, whereas closures are invoked — potentially — far away from their point of definition. That's why functions use, but closures capture, the relevant parts of their surrounding environment. A closure is similar to a C-style callback, except that instead of needing a separate "context" parameter, it has its own context built in. In Swift, a function (including a method) is semantically just a closure with a static name — you can pass a function name anywhere that a closure parameter is required, assuming that the type signature is correct. The compiler takes care of smoothing over the difference between "use" and "capture".
- Question 17Senior
What is the difference between ARC (automatic reference counting) and GC (garbage collection)?
- Objective-C iOS Swift
Reveal answerHide answer
ARC rely on a compile time "referenced" objects which make it efficient in a low-power mode environments (Mobile devices). ARC injects a code into the executable to be executed "automatically" on unused objects depending on their reference count. Advantages: Real-time, deterministic destruction of objects as they become unused. No background processing. Disadvantages: ARC cannot handle retain cycles automatically. GC rely on a runtime based "reachable" objects which make it efficient in a multi-threaded environment. GC works in the runtime as it will detect the unused object graphs (will eliminate retain-cycles) and remove them on an indeterminate time intervals Advantages: GC can clean up entire object graphs, including retain cycles. GC proceed in the background, so less memory management work is done as part of the regular application flow. Disadvantages: Because GC happens in the background, the exact time frame for object releases is undetermined. When a GC happens, other threads in the application may be temporarily put on hold.
- Question 17Senior
What is the difference between static func and class func in Swift?
- Swift
Reveal answerHide answer
static func is same as final class func Consider:
class ClassA { class func func1() -> String {return "func1"
} static func func2() -> String {return "func2"
}/* same as above final class func func2() -> String {return "func2"
} */ }Note because func2 is final , we can not override it in subclass as below:
class ClassB : ClassA { override class func func1() -> String {return "func1 in ClassB"
}// ERROR: Class method overrides a 'final` class method override static func func2() -> String {return "func2 in ClassB"
} }Also check that:
- Question 18Senior
What is the autoclosure attribute and when to use it?
- Swift
Reveal answerHide answer
Swift’s @autoclosure attribute enables you to define an argument that automatically gets wrapped in a closure. It’s primarily used to defer execution of a (potentially expensive) expression to when it’s actually needed, rather than doing it directly when the argument is passed. As an usage example consider the conditional expression passed as the first parameter to until will be automatically wrapped up into a closure expression and can be called each time around the loop
func until<L: LogicValue>(pred: @auto_closure ()->L, block: ()->()) { while !pred() { block() } }// doSomething until condition becomes true until(condition) { doSomething() } - Question 18Senior
What is the difference between fileprivate and private ?
- Swift
Reveal answerHide answer
fileprivate can be accessed from the their entire files. Restricts the use of an entity to its defining source file. private can only be accessed from their single declaration and to extensions of that declaration that are in the same file; For instance:
// Declaring "A" class that has the two types of "private" and "fileprivate": class A { private var aPrivate: String? fileprivate var aFileprivate: String? func accessMySelf() { // this works fine self.aPrivate = "" self.aFileprivate = "" } }// Declaring "B" for checking the abiltiy of accessing "A" class: class B { func accessA() { // create an instance of "A" class let aObject = A() // Error! this is NOT accessable... aObject.aPrivate = "I CANNOT set a value for it!" // this works fine aObject.aFileprivate = "I CAN set a value for it!" } } - Question 19Senior
What is QoS (Quality of Service) in GCD?
- Swift iOS
Reveal answerHide answer
Defining quality of service helps us categorize the task of our DispatchQueue with priority. By using it the right way, we could make our app energy efficient and responsive. QoS can be used with .async() function, and the priorities are divided into four main categories. userInteractive: Used for animations, or updating UI. userInitiated: Used for tasks like loading data from API, preventing the user from making interactions. utility: Used for tasks that do not need to be tracked by the user. background: Used for tasks like saving data in the local database or any maintenance code which is not on high priority. Consider:
let queue = DispatchQueue(label: "com.swiftpal.dispatch.qos")// async with background type queue.async(qos: .background) { print("Background Code") }// async with userInitiated type queue.async(qos: .userInitiated) { print("User Initiated Code") } - Question 19Senior
What is the difference between open and public keywords in Swift?
- Swift
Reveal answerHide answer
In short: An open class is accessible and subclassable outside of the defining module. An open class member is accessible and overridable outside of the defining module. The reasoning there is some classes of libraries and frameworks are not designed to be subclassed and doing so may result in unexpected behavior. A public class is accessible but not subclassable outside of the defining module. A public class member is accessible but not overridable outside of the defining module.
// First.framework – A.swift open class A {} // First.framework – B.swift public class B: A {} // ok // Second.framework – C.swift import First internal class C: A {} // ok // Second.framework – D.swift import First internal class D: B {} // error: B cannot be subclassed
Expert questions
13 questions- Question 2Expert
What is the use of Hashable protocol?
- Swift
Reveal answerHide answer
In Swift, the Hashable protocol is the main component used to provide hashing capabilities to custom types. It is most commonly used to allow types to be used as the key of Dictionaries and Sets . If an object conforms to the hashable protocol, it needs to have a hashValue . The hashValue can be used to compare objects / uniquely identify the object. You can compare objects in two ways:
- === function. This checks object references (can only be used with classes). It checks if the left object has the same reference to the right object. Even if both objects have exactly the same property values BUT they do have a different reference, it returns false.
- == function ( Equatable protocol). It checks if the objects are equal to eachother based on the static func == . You can return the hashValue of the object. In that way, you can say objects are equal to eachtoher based on the properties, rather than reference.
If you provide your own hashValue , you can say objects are equal to eachother in a way you say objects are equal to eachother, regardless of the reference to the object. You can use objects in a Set that conform to the hashable protocol, because a Set checks if objects are equal to eachother based on the hashValue .
- Question 3Expert
What’s the difference between init?() and init() ?
- Swift
Reveal answerHide answer
init?() is a failable initializer and can return nil rather than a value if something goes wrong during creation. Consider:
struct Person { var ssn: String init?(socialSecurityNumber: String) { if socialSecurityNumber.count < 11 {return nil
} else { self.ssn = socialSecurityNumber } } } The initializer is now called init?() to reflect that it returns an optional – the process might return nil if the creation fails. - Question 4Expert
What is the Never return type? When to use it over Void ?
- Swift
Reveal answerHide answer
Void Void is itself a return type which is a tuple with zero elements. You can use Void and () interchangeably. Look at these examples,
- func yourFunc() {} This is a function without a return type, which basically returns a tuple with zero elements, that can be written as ()
- func yourFunc() -> Void {} Function which is explicitly informing the compiler about return type of void
- func yourFunc() -> () {} This return type of () displays the same as void type. () indicates a tuple with zero elements Never Never return-type informs the compiler that no need exists to return an empty tuple (). Also, function with the never return type is used for the exit point of the current execution like a crash, fatal error, abort or exit. Consider:
public func abortVoid() -> Void { fatalError() } public func abortNever() -> Never { fatalError() } func bar1() -> Int { if true { abortVoid() // ERROR: Missing return in a function expected to return 'Int' } else {return 1
} } func bar2() -> Int { if true { abort() // No ERROR, but compiler sees it returns Never and warns:return 2
// Will never be executed } else {return 1
} } - Question 5Expert
How to deal with retain cycles in Closures? Provide an example.
- Swift
Reveal answerHide answer
You always need to be mindful of memory management when using closures. Like classes, closures arereference types. Because a closure can capture values from its surrounding scope, a closure can introduce a reference cycle. Consider:
class C { var f: (Void -> Void)! deinit { print("deinit for C") } } do { let c = C() c.f = { print(c) } }c captures f , f captures c . So we got memory leak. To deal with leaks in closures, you have 2 options – declare that captured object is weak or unowned . Like this:
do { let c = C() c.f = { [weak c] in print(c) } }Basically, you would use weak if it's possible for object to get out of existence and become nil when closure is called; but if you are sure object will still exist at this time, use unowned instead.
- Question 7Expert
Why can not we just use weak everywhere and forget about unowned ?
- Swift
Reveal answerHide answer
A weak reference is more accurately named an auto-zeroing weak reference. The system knows about an object stored in a weak variable, and when the object is deallocated, goes to the extra work to zero out the
value. As a consequence of that, weak variables are optionals in Swift. Unowned pointers are not memory managed at all. They are a raw memory pointer that can take any value. (They can still be typed to point to a particular type of structure, so they aren't always "raw" in the way a C void pointer is raw.) Unowned pointers are useful for pointing to malloc'ed memory, pointer arithmetic, and the like. Declaring a pointer type as unowned basically tells the compiler "Don't worry about it. I know what I'm doing." The burden is then on you to make sure the pointer is always valid, before you try to dereference it, and also manage the memory behind the pointer yourself.
- Question 8Expert
Explain the use case when ARC won't help you to release memory (but GC will)?
- Objective-C iOS Swift
Reveal answerHide answer
As an example, if you have a class named node, and node has an NSArray of children, and a single reference to its parent that "just works" with GC. With ARC (and manual reference counting as well) you have a problem. Any given node will be referenced from its children and also from its parent. Like:
A -> [B1, B2, B3] B1 -> A, B2 -> A, B3 -> A All is fine while you are using A (say via a local variable).When you are done with it (and B1/B2/B3), a GC system will eventually decide to look at everything it can find starting from the stack and CPU registers. It will never find A,B1,B2,B3 so it will finalize them and recycle the memory into other objects. When you use ARC or MRC, and finish with A it have a refcount of 3 (B1, B2, and B3 all reference it), and B1/B2/B3 will all have a reference count of 1 (A's NSArray holds one reference to each). So all of those objects remain live even though nothing can ever use them. The common solution is to decide one of those references needs to be weak (not contribute to the reference count). That will work for some usage patterns, for example if you reference B1/B2/B3 only via A. However in other patterns it fails. For example if you will sometimes hold onto B1, and expect to climb back up via the parent pointer and find A. With a weak reference if you only hold onto B1, A can (and normally will) evaporate, and take B2, and B3 with it. You can also say ARC puts a bigger priority on performance (or maybe predictability) while GC puts a bigger priority on being a generic solution. As a result GC has less predictable CPU/memory demands, and a lower
performance (normally) than ARC, but can handle any usage pattern. ARC will work much better for many many common usage patterns, but for a few (valid!) usage patterns it will fall over and die. - Question 9Expert
Explain what is DispatchGroup?
- Swift
Reveal answerHide answer
DispatchGroup allows for aggregate synchronization of work. You can use them to submit multiple different work items and track when they all complete, even though they might run on different queues. This behavior can be helpful when progress can’t be made until all of the specified tasks are complete. A scenario when this could be useful is if you have multiple webservice calls that all need to finish before continuing. For example, you need to download multiple sets of data that needs to be processed by some function. You have to wait for all webservices to complete before calling the function to process all the received data.
func doLongTasksAndWait () { print("starting long running tasks") let group = DispatchGroup() //create a group for a bunch of tasks we are about to do for i in 0...3 { //launch a bunch of tasks (eg a bunch of webservice calls that all need to be finished before proceeding to the next ViewController) group.enter() //let the group know that something is being added DispatchQueue.global().async { //run tasks on a background thread sleep(arc4random() % 4) //do some long task eg webservice or database lookup (here we are just sleeping for a random amount of time for demonstration purposes) print("long task \(i) done!") group.leave() //let group know that the task is finished } } group.wait() //will block whatever thread we are on here until all the above tasks have finished (so maybe dont use this function on your main thread) print("all tasks done!") }Alternatively, if you do not want to wait for the groups to finish, but instead want to run a function once all the tasks have completed, use the notify function in place of the group.wait()
group.notify(queue: DispatchQueue.main) { //the queue: parameter is which queue this block will run on,if you need to do UI updates, use the main queue
print("all tasks done!") //this will execute when all tasks have left the group } - Question 10Expert
What are the benefits of using DispatchWorkItem in Swift?
- Swift iOS
Reveal answerHide answer
The DispatchWorkItem class is an encapsulation of the concept of work item. By encapsulating our request code in a work item, we can very easily cancel it whenever it's replaced by a new one, like this:
class SearchViewController: UIViewController, UISearchBarDelegate { // We keep track of the pending work item as a property private var pendingRequestWorkItem: DispatchWorkItem? func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) { // Cancel the currently pending item pendingRequestWorkItem?.cancel() // Wrap our request in a work item let requestWorkItem = DispatchWorkItem { [weak self] in self?.resultsLoader.loadResults(forQuery: searchText) } // Save the new work item and execute it after 250 ms pendingRequestWorkItem = requestWorkItem DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(250), execute: requestWorkItem) } } - Question 11Expert
Explain usage of Concurrent vs Serial Queues with async and sync blocks
- iOS Swift
Reveal answerHide answer
- the queue's type ( Concurrent or Serial) is the ONLY element that decides whether the tasks are executed in order or in paralel;
- the dispatch type ( sync or async ) is only saying whether the execution goes OR doesn't go to the next instruction The queue dictates the execution policy, not how you queue the block. sync waits for the block to complete, async doesn't. A simple example: you have a block that takes a minute to execute. You add it to a queue from the main thread. Let's look at the four cases. async - concurrent: the code runs on a background thread. Control returns immediately to the main thread (and UI). The block can't assume that it's the only block running on that queue async - serial: the code runs on a background thread. Control returns immediately to the main thread. The block can assume that it's the only block running on that queue sync - concurrent: the code runs on a background thread but the main thread waits for it to finish, blocking any updates to the UI. The block can't assume that it's the only block running on that queue (I could have added another block using async a few seconds previously) sync - serial: the code runs on a background thread but the main thread waits for it to finish, blocking any updates to the UI. The block can assume that it's the only block running on that queue
- Question 12Expert
Explain the use case when ARC won't help you to release memory (but GC will)?
- Objective-C iOS Swift
Reveal answerHide answer
As an example, if you have a class named node, and node has an NSArray of children, and a single reference to its parent that "just works" with GC. With ARC (and manual reference counting as well) you have a problem. Any given node will be referenced from its children and also from its parent. Like:
A -> [B1, B2, B3] B1 -> A, B2 -> A, B3 -> A All is fine while you are using A (say via a local variable).When you are done with it (and B1/B2/B3), a GC system will eventually decide to look at everything it can find starting from the stack and CPU registers. It will never find A,B1,B2,B3 so it will finalize them and recycle the memory into other objects. When you use ARC or MRC, and finish with A it have a refcount of 3 (B1, B2, and B3 all reference it), and B1/B2/B3 will all have a reference count of 1 (A's NSArray holds one reference to each). So all of those objects remain live even though nothing can ever use them. The common solution is to decide one of those references needs to be weak (not contribute to the reference count). That will work for some usage patterns, for example if you reference B1/B2/B3 only via A. However in other patterns it fails. For example if you will sometimes hold onto B1, and expect to climb back up via the parent pointer and find A. With a weak reference if you only hold onto B1, A can (and normally will) evaporate, and take B2, and B3 with it. You can also say ARC puts a bigger priority on performance (or maybe predictability) while GC puts a bigger priority on being a generic solution. As a result GC has less predictable CPU/memory demands, and a lower
performance (normally) than ARC, but can handle any usage pattern. ARC will work much better for many many common usage patterns, but for a few (valid!) usage patterns it will fall over and die. - Question 12Expert
What is the difference between @escaping and @nonescaping Closures in Swift?
- Swift
Reveal answerHide answer
A non-escaping closure is a closure that is called within the function it is passed into. The execution of the closure will be before the function returns Pass the closure as a function argument The function processes some work The function runs the closure The function returns
let add: (Int,Int) -> () = { a,b in print("Result \(a + b)") } func mathForOneAndTwo(firstNum: Int, secondNum: Int, action: (Int,Int) -> Void) { action(firstNum,secondNum) } mathForOneAndTwo(firstNum: 1, secondNum: 2, action: add)An escaping closure is a closure that executes after the function it is passed into returns. The closure outlives the function that it is passed into, and this is known as escaping. escaping closures are frequently used for asynchronous execution or storage. Pass the closure as a function argument The function processes some work The function returns At some later stage, the closure is executed
let add: (Int,Int) -> () = { a, b in print("Result \(a + b)") } func mathForOneAndTwo(firstNum: Int, secondNum: Int, action: @escaping (Int,Int) -> Void) { DispatchQueue.main.asyncAfter(deadline: .now() + 2, execute: { action(firstNum,secondNum) } ) } mathForOneAndTwo(firstNum: 1, secondNum: 2, action: add)Closures themselves are reference types, and by default they are non-escaping closures.
- Question 13Expert
Explain usage of Concurrent vs Serial Queues with async and sync blocks
- iOS Swift
Reveal answerHide answer
- the queue's type ( Concurrent or Serial) is the ONLY element that decides whether the tasks are executed in order or in paralel;
- the dispatch type ( sync or async ) is only saying whether the execution goes OR doesn't go to the next instruction The queue dictates the execution policy, not how you queue the block. sync waits for the block to complete, async doesn't. A simple example: you have a block that takes a minute to execute. You add it to a queue from the main thread. Let's look at the four cases. async - concurrent: the code runs on a background thread. Control returns immediately to the main thread (and UI). The block can't assume that it's the only block running on that queue async - serial: the code runs on a background thread. Control returns immediately to the main thread. The block can assume that it's the only block running on that queue sync - concurrent: the code runs on a background thread but the main thread waits for it to finish, blocking any updates to the UI. The block can't assume that it's the only block running on that queue (I could have added another block using async a few seconds previously) sync - serial: the code runs on a background thread but the main thread waits for it to finish, blocking any updates to the UI. The block can assume that it's the only block running on that queue
- Question 20Expert
What's the difference between marking a method as @objc vs dynamic , when would you do one vs the other?
- Objective-C Swift
Reveal answerHide answer
A function/variable declared as @objc is accessible from Objective-C, but Swift will continue to access it directly via static or virtual dispatch. This means if the function/variable is swizzled via the Objective-C framework, like what happens when using Key-Value Observing or the various Objective-C APIs to modify classes, calling the method from Swift and Objective-C will produce different results. Using dynamic tells Swift to always refer to Objective-C dynamic dispatch. This is required for things like Key- Value Observing to work correctly. When the Swift function is called, it refers to the Objective-C runtime to dynamically dispatch the call.