This idea come out when I start to prepare my iOS interview question. In this open source repository, I am trying to gather the related iOS interview question as much as I can. Then, I will use my own word to define/answer those of the question in order to examine my knowledge. I also recommend some book or resouce that I have read to prepare my interview, and I list below.
- 100 Questions and Answers to Help You Land Your Dream iOS Job: Or to Hire the Right Candidate!
- Top 50 Swift Interview Questions
- Swift Interview Questions and Answers - Raywenderlich
- Hacking with the Swift - Over 150 iOS interview questions
-
- What are struct and class in iOS? What is the difference? When would you use each?
- What is framework in iOS? & What iOS frameworks do you have experience working with?
- How can you store information within your iOS app?
- How do you cast between types?
- What is the difference between var and let?
- What is the difference between implicit and explicit?
- Tell me about enum in Swift language. Can you give useful examples of enum associated values?
- What is Core Data?
- When would you use Core Data over NSUserDefault?
- What is NSFetchRequest?
-
- How do you debug and profile code on iOS application?
- How do you optimize scrolling performance of dynamically sized table or collection views?
- Explain your process for tracing and fixing memory leaks.
- You’ve just been alerted that your new app is prone to crashing. What do you do?
- How do you test your code? How do you make your code testable? (Todo)
-
- Why do you generally create a weak reference when using self in a block?
- What is memory management handled on iOS?
- What is the difference between weak and strong?
- What is a memory leak?
- What is a retain cycle?
- What is the difference between copy and retain?
- What is the difference between a stack vs a heap?
- What are “strong” and “weak” references? Why are they important and how can they be used to help control memory management and avoid memory leaks?
The common parts between struct and class:
- Both can define properties and functions in classes/structs
- Both can has initializers to initialize thier original state with init()
- Can use extension to extended the functionality
- Both can conform to protocols.
The stuct is Value type. In Swift, the struct type will be passed as values rather than references which means value type is an independent instance and holds its data in its own memory allocation. One of the Pros is if there is a large object it needs to pass around. Using structs can improve the performance.
In Swift struct, Array, String, Dictionary, tuple are value types.
Structs are preferable to use if
- It doesn't have to inherit value from other types.
- When programmer only want to encapsulate some data.
Struct Example
struct Bike {
let brand: String
var model: String
}
var bike1 = Bike(brand: "Giant", model: "G-1")
let bike2 = bike1
bike1.model = "G-2"
print(bike1)
print(bike2)
/*
Output:
Bike(brand: "Giant", model: "G-2")
Bike(brand: "Giant", model: "G-1")
Note: Only Bike1's model has changed.
*/
The class is reference types which stored on heap. The class also has ability:
- Inheritance from the other class.
- Classes can be deinitialized.
Class Example
class Person: CustomStringConvertible {
var age: Int
var name: String
var description: String {
return "Age \(age) - Name \(name)"
}
init(age: Int, name: String) {
self.age = age
self.name = name
}
}
let person1 = Person(age: 20, name: "Hung-Chun")
let person2 = person1
person2.age = 18
person1.name = "Carter"
print(person1)
print(person2)
/*
Output:
Age 18 - Name Carter
Age 18 - Name Carter
Note: Because class is reference type, when we assign the person2 = person1, we indicate that person2 point to the person1's address. So when we modify the person1 or 2's property value, it will modify both's.
*/
The framework in iOS is like the container with the various resources (images, data files, UI objects, etc.) Framework also can be library or containing many libraries, collection of scripts.
There are many different ways that we can achieve data persistence in Swift
-
UserDefault UserDefault usually store small amount of user's data like (settings, preferences, etc.) -Limitation :
- UserDefault is not encrypted
- Unit testing user defaults can occur some false positives.
-
Keychain It usually use to save the sensitive user data. For example: Password, login information... -Pros:
- Thread safety
- All information in keychain is encrypted
-
Saving file to disk We can also encode the data to the JSON file then save it to the user's local device. When the application is running or execute, it will read/write the data from the path.
-
Core Data Core Data is Apple's persistency framework which based on an object graph. It can uss for saving the app's permanet data for offline use or cache the data.
-
SQLite
Apple - A constant or variable of a certain class type may actually refer to an instance of a subclass behind the scenes. Where you believe this is the case, you can try to downcast to the subclass type with a type cast operator (as? or as!).
Downcasting can be done in two ways:
- Conditional downcasting (as?).
- Forced downcasting (as!)
Conditional Downcasting While using the conditional downcasting, it will always return an optional value. That's mean when the downcasting is not available, it will always return nil.
Forced Downcasting When we use the Forced Downcasting, we need to make sure that the downcast will always succeed. Otherwise, it will occure runtime error if we try to downcast to an unvailable class type.
Upcasting can lets the base class object to cast to its superclass.
class PizzaStore {
var establish :Int?
init(establish:Int?) {
if let establish = establish {
self.establish = establish
}
}
}
class Dominos: PizzaStore {
var name: String?
init(name:String?, establish:Int?) {
if let name = name {
self.name = name
}
super.init(establish: establish ?? 0)
}
}
class PizzaHut: PizzaStore {
var hasApplePie: Bool?
init(hasApplePie:Bool?, establish:Int?) {
if let hasApplePie = hasApplePie {
self.hasApplePie = hasApplePie
}
super.init(establish: establish ?? 0)
}
}
let pizzaStoreArray = [Dominos(name: "Domino's", establish: 1960), PizzaHut(hasApplePie: false, establish: 1958)]
let dominoPizzaStore = pizzaStoreArray[0] as! Dominos
let dominoPizzaStoreAsPizzaStore = dominoPizzaStore as PizzaStore
The let keyword defines a constant which means the property that we don't want it to change their value.
Note: The optional and the weak can't be written using let.
The var defines a variable which can let us to modify it's value.
When referring to something as implicit or explicit, it is often referring to how an object is declared. In the two examples below:
Swift
var name: String = "onthecodepath" // explicit
var name = "onthecodepath" // implicit
In the first line above, the name variable is explicitly declared since the type of the variable follows the name of the variable. In the second line, the String type is not explicitly declared. However, Swift is able to infer that name is of a String type since the value that it is being set as is of a String type.
An enumeration (enum) is value type in a type-safe way. It is not mandotary to provide the initial value for the enumeration.
Here’s an example for the four main points of a compass:
enum Direction {
case north
case south
case east
case west
}
Note: Swift enums don’t have integer values set by default. In example above north, south, east, west don’t implicitly equal 0 ,1 ,2 ,3. Instead these enumeration cases are values in their own right of type Direction.
Important: A swich statement must be exhaustive if we want to use the enumeration in it. If we use the default, it is not nessasary to include all the cases.
let someAnimal = Animal.human
switch somePlanet {
case .human:
print("This is human species")
default:
print("The animal is other than human")
//It is no need to include all the cases in the Animal enumeration.
}
// Prints "The animal is other than human"
- Associate values lets us add more information to our enumeration.
The scenario is "In my SwiftUI application, I want to present different views modal, when I selected the certain button." In this case, we can use the enum associate value helps us to tackle down this problem.
Requirement:
- We have 3 different view we want to provide to user to select
- Home View
- Favorite View
- Setting View
Step 1. Let us define the MyOption which will contain those three views
//We want to present the implicitly our each view name, so we need to conform to String and CaseIterable.
enum UserOption: String, CaseIterable {
case setting = "Setting View"
case action1 = "Action 1"
case action1 = "Action 2"
}
Step 2. Let's wrap the UserOption enum into the other enum because we will use the function to determine which case user is selected and generate the corresponding view.
enum OtherViews {
enum UserOption: String, CaseIterable {
case setting = "Setting View"
case action1 = "Action 1"
case action2 = "Action 2"
}
static func optionView(optionSelected: UserOption) -> AnyView {
switch optionSelected {
case .setting:
return AnyView(Text("Showing Setting View"))
case .action1:
return AnyView(Text("Showing Action1 View"))
case .action2:
return AnyView(Text("Showing Action2 View"))
}
}
}
Step 3. Let create our view to include those fucntionality.
import SwiftUI
struct ContentView: View {
@State private var isShowingModal = false
@State private var optionSelected: OtherViews.UserOption = .action1
var body: some View {
VStack {
Picker(selection: $optionSelected, label: Text("")) {
ForEach(OtherViews.UserOption.allCases, id: \.self) { option in
Text(option.rawValue.capitalized).tag(option)
}
}.pickerStyle(SegmentedPickerStyle())
Button("Show Modal") {
self.isShowingModal.toggle()
}
.sheet(isPresented: $isShowingModal) {
OtherViews.optionView(optionSelected: optionSelected)
}
}
.padding()
}
}
struct ContentView_Previews: PreviewProvider {
static var previews: some View {
ContentView()
}
}
Core Data is a framework that is used to manage model layer objects (Like the M in MVC). It has the ability to persist object graphs to a persistent store. Data is organized into relational entity-attribute model.
- Most of time, NSUserDefault is used to store small quantity of data (settings, preferences, etc.). The UserDefault object known as NSUserDefault.
- Core Data is used to store a large amount of data.
NSFetchRequest is the class responsible for fetching from Core Data. Fetch requests can be used to fetch a set of objects meeting a certain criteria, individual values and more. (source)
- Using NSLog and print functions can be used for output into console.
- Set the breakpoints and using the Debug bar and Variables view to monitor the object or class.
- Some Senior Developer will use the Crash Logs to gain more information and try to find out the specific problem.
Firstly, I will analyze which element or reason cause the choppy loading while using the collection views. Most of the time it is because we load the data (Image, text, object) synchronously.
Imangine this scenario in the restaurant. In the restarant there are 3 chief but only have one server. The customer order the bunch of the dishes (Just like user scrolling down the collection view). Sometime the kitchen (backend) might able to handle this big volume, but we only open one thread to serve our content which is data.
So we might can do following strategies to improve the performence.
- Splitting out the long-running operations and move them to a background thread. This allows us to handle any events when they happen, on the main thread. When the background operation completes, you can make any required UI updates, based on the operation, on the main thread.
- **Loading the data asynchronously ** We can use Grand Central Dispatch (GCD) to execute tasks concurrently. The main idea is to create a closure to handle how the cell is updated once the data is loaded.
- Prefetching the data The problem occur while we fetch big amount of the data. But we also can try to prefethching the oncoming data. For example, the view only present the data 1-10, meanwhile we let our program already fetch the next 11-20 and 21-30 items. Whenever the user scroll down, we continually fetching the oncoming data.
I will use the Memory Graph Debugger and try to find out which object causing the memory leak.
Some step I will reaptly do:
- Execute the core/main functionality of the application serveral times.
- Take the memory snapshot and observe how many instance it exist for each object.
- If I found out there has instance shouldn't existing in the memory anymore, it might be the object which causing the memory leak.
- Try to track down it's parent node and analyze the code which lead the strong reference coming.
There are some steps that I will attemp.
- If the user willing to sent their device model, iOS version it will be a big help. Try to indicate whether certain model or version of iOS cause this crashing.
- If it is possible, try to reproduce the error and using different simulator to find out the issue.
- If it's possible, request the user to provide the detail of which action cause the crashing/error.
The Singleton design pattern ensures that only one instance exists for a given class and that there’s a global access point to that instance. It usually uses lazy loading to create the single instance when it’s needed the first time. (source)
The delegation pattern is a powerful pattern used in building iOS applications. The basic idea is like the delegate the mission/task to the worker. One object will act on another object's behalf or in coordination with another object.
In this scenario, the 'PizzaStore' in charge of assigning task to worker, and 'PizzaStore' delegate making the pizza dough to 'Worker'. Once worker done then give the baked pizza to manager, so PizzaStore can get the pizza.
import SwiftUI
struct ContentView: View {
struct Pizza {
var size:Int = 15
}
protocol PizzaStoreDelegate {
func pizzaWasBaked(_ pizza: Pizza)
}
class PizzaStore
{
var delegate:PizzaStoreDelegate?
func makePizza()
{
var pizza = Pizza()
pizza.size = 6
delegate?.pizzaWasBaked(pizza)
}
}
class Worker: PizzaStoreDelegate
{
func pizzaWasBaked(_ pizza: Pizza) {
print("The worker start produce the pizza")
print("The pizza has baked by worker")
}
}
let worker = Worker()
let pizzaStore = PizzaStore()
pizzaStore.delegate = worker
pizzaStore.makePizza()
/*
Output:
The worker start produce the pizza
The pizza has baked by worker
*/
MVC stands for Model-View-Controller. It is a software architecture pattern for implementing user interfaces.
MVC consists of three layers: the model, the view, and the controller.
- The model layer is typically where the data resides (persistence, model objects, etc)
- The view layer is typically where all the UI interface lies. Things like displaying buttons and numbers belong in the view layer. The view layer does not know anything about the model layer and vice versa.
- The controller (view controller) is the layer that integrates the view layer and the model layer together.
MVVM stands for Model-View-ViewModel. It is a software architecture pattern for implementing user interfaces.
MVVM is an augmented version of MVC where the presentation logic is moved out of the controller and into the view model. The view model is responsible for handling most, if not all, of the view's display logic.
A common occurence in MVC is where you have a massive-view-controller (some joke this is what MVC stands for). In order to shrink the size of your view controller and make the logic and readibility of your code easier to follow along, the MVVM will be used.
What considerations do you need when writing a UITableViewController which shows images downloaded from a remote server?
- Only download the image when the cell is scrolled into view (when cellForRowAtIndexPath is called)
- Download the image asynchronously on a background thread so as not to block the UI so the user can keep scrolling
- When the image has downloaded for a cell, check if that cell is still in the view or whether it has been re-used by another piece of data. If the cell has been re-used, then the image should be discarded. Otherwise, it should be switched back to the main thread to change the image on the cell. (source)
A protocol defines a list of required and optional methods for a class that adopts the protocol to implement. Any class is allowed to implement a protocol so that other classes can send message to it based on the protocol methods without knowing the type of class. An example of how a protocol is defined:
A common instance protocols are used is providing a DataSource for UITableView or UICollectionView (source)
JSON stands for JavaScript Object Notation. According to wiki, it is a file format that uses human-readable text to transmite data objects consisting of attribute-value pairs and array data types.
Pros:
- It is lighter than XML meaning that it can represent the same data in XML in fewer bytes. This makes network transmissions and read/writes faster
- Since it is native to JavaScript, computationally-expensive XSL tranformations are not needed in order to extract data
Cons:
- Not as widespread as XML
- Data is not readily streamable and has to be broken up into individual objects
- Can't use comments
What is the difference between not-running, inactive, active, background and suspended execution states?
- Not-running state occurs when the app either has not be launched or was running but was terminated by the system.
- Inactive state occurs where the app runs in the foreground but is currently not receiving events. (It may be executing other code though). This state is typically brief as apps transitions to other states.
- Active state is where the app is running in the foreground and receiving events. This is the normal mode for foreground apps.
- Background state occurs when the app is in the background and executing code. Apps typically enter this state on their way to being suspended. Apps that require extra execution time may remain in this screen longer. Apps being launched directly into the background enters this state instead of inactive state.
- Suspended state is where the app is in the background but it is not executing code. Apps will remain in memory, but are removed by the system if low-memory condition occurs in order to make more space for foreground apps.
It depends. NSSet is faster to iterate through if the order of the items in the collection is not important. The reason is because NSSet uses hash values in order to find items while NSArray has to iterate through its entire contents to find a particular object. (source - #25)
KVO stands for Key-Value Observing. It allows a controller or class to observe when a property value changes.
Sometimes it is necessary it capture self in a block such as when defining a callback block. However, since blocks maintain strong references to any captured objects including self, this may lead to a strong reference cycle and memory leak.
Instead, capturing a weak reference to self is recommended in order to avoid this issue
iOS uses something called ARC which stands for Automatic Reference Counting. When an object is said to have a strong reference to it, ARC increase its retain count by 1. When the retain count of an object reaches 0, the object will typically be deallocated if there are no more strong references to it. Unlike garbage collection, ARC does not handle reference cycles automatically.
What is ARC?
Automatic Reference Counting (ARC) provides automatic memory management for all Objective-C object. Developer no need to manually reain and release operations.
First, objects are strong by default.
- Strong means that the reference count will be increased and the reference to it will be maintained through the life of the object.
- Weak, means that we are pointing to an object but not increasing its reference count. It’s often used when creating a parent child relationship. The parent has a strong reference to the child but the child only has a weak reference to the parent. (source)
Common instances of weak references are delegate properties and subview/controls of a view controller's main view since those views are already strongly held by the main view. (source)
A memory leak commonly occurs when an object is allocated in such a way that when it is no longer in use or needed, it is not released. In iOS programming, you create certain objects with weak references in order to avoid a strong to strong relationship that creates a retain cycle and a memory leak.
Retain cycles can occur when memory management is based on retain count. This typically occurs when two objects strongly reference each other. As a result, the retain count of either object will never reach zero and deallocated from memory (hence retaining each other).
Calling retain on an object will increase its retain count by one. When the retain count of an objective reaches 0, the object will be deallocated and released from memory.
When you retain an object, you share the same version with whoever passed the object to you. But when you copy an object, you do not share the same version of the object that was passed to you. Instead, a duplicate of that object is created with duplicated values.
A stack is a region of memory where data is added or removed in a last-in-first-out (LIFO) order. According to Ates Goral, it is the memory set aside as scratch space for a thread of execution. Meanwhile the heap is memory set aside for dynamic allocation. Unlike the stack, you can allocate a block at any time and free it at anytime.
Note: In Objective-C, all objects are always allocated on the heap, or at least should be treated as if on the heap.
What are “strong” and “weak” references? Why are they important and how can they be used to help control memory management and avoid memory leaks?
When developer create the objects the default references is set as strong, and this will prevents the ARC(Automatic Reference Count) from removing the object from memory. If we don't deinitialized the object while we are not using, this will cause the memory leak. Upon time, the application might occur the error which is run out of the memory.
So the weak reference is to slove this issue, when we create a weak reference we are pointing to an object without increasing reference count.
Common instances of weak references are delegate properties and subview/controls of a view controller's main view since those views are already strongly held by the main view. (source)
Synchronous tasks wait until the task has been completed while asynchronous tasks can run in the background and send a notification when the task is complete.
GCD stands for Grand Central Dispatch. According to Ray Wenderlich, it offers the following benefits
- Improving your app's responsiveness by helping to defer computationally expensive tasks and run them in the background.
- Providing an easier concurrency model than locks and threads and helps to avoid concurrency bugs.
- Potentially optimize your code with higher performance primitives for common patterns such as singletons.
In other words, GCD provides and manages queues of tasks in the iOS app. This is one of the most commonly used API to manage concurrent code and execute operations asynchronously. Network calls are often performed on a background thread while things like UI updates are executed on the main thread.
Unit/UI testing are the basic of test-driven development. This development approach allows you to codify requirements for your code before you implement it. Unit tests are important to make sure that code meets its design and requirements and behaves as expected. Parts of the program are segregated and tested to ensure that individual parts are working correctly.