iOS interview questions 2018
iOS 2018 series — Cracking iOS interview or Become iOS expert (2)
Chapter 2: KVC & KVO
I’m going to explain the concepts behind bindings from the ground up; first explaining Key-Value Coding (KVC), then Key-Value Observing (KVO).
Keys and key paths are frequently used for key-value coding (KVC), a mechanism for indirectly accessing an object’s attributes and relationships using string identifiers. Keys and key paths are also used for key-value observing (KVO), a mechanism that enables an object to be notified directly when a property of another object changes.
Key-Value-Coding (KVC) means accessing a property or value using a string.
id someValue = [myObject
valueForKeyPath:@"foo.bar.baz"];
Which could be the same as:
id someValue = [[[myObject foo] bar] baz];
Key-Value-Observing (KVO) allows you to observe changes to a property or value.
To observe a property using KVO you would identify to property with a string; i.e., using KVC. Therefore, the observable object must be KVC compliant.
[myObject addObserver:self
forKeyPath:@"foo.bar.baz" options:0
context:NULL];
Lets dive into some details:
Key-Value Coding (KVC)
Actually, any property you want to observe for changes must be
Key-Value Coding compliant, An object is key-value coding
compliant when it adopts the
NSKeyValueCoding
protocol. An object that inherits from
NSObject
, which provides a default implementation of the protocol’s
essential methods, automatically adopts this protocol with
certain default behaviors. Lets start with an example to
understand it.
@interface
BankAccount : NSObject
@property (nonatomic) NSNumber*
currentBalance; // An attribute
@property (nonatomic) Person*
owner; // A to-one relation
@property (nonatomic) NSArray< Transaction* >*
transactions; // A to-many relation
@end
In order to maintain encapsulation, an object typically provides
accessor methods for the properties on its interface. The
object’s author may write these methods explicitly or may rely
on the compiler to synthesize them automatically. Either way,
the author of code using one of these accessors must write the
property name into the code before compiling it. The name of the
accessor method becomes a static part of the code that is using
it. For example, given the bank account object declared above,
the compiler synthesizes a setter that you can invoke for the
myAccount
instance:
[myAccount setCurrentBalance:@(100.0)]; //NORMAL
[myAccount setValue:@(100.0) forKey:@”currentBalance”]; //KVC
This is direct, but lacks flexibility. A key-value coding compliant object, on the other hand, offers a more general mechanism to access an object’s properties using string identifiers.
Keys
A key is a string that identifies a specific property.
BankAccount
class is key-value coding compliant, it recognizes the keys
owner
,
currentBalance
,
and
transactions
,
which are the names of its properties. Instead of calling the
setCurrentBalance:
method, you can set the value by its key:
[myAccount setValue:@(100.0) forKey:@”currentBalance”];
In fact, you can set all the properties of the
myAccount
object with the same method, using different key parameters.
Key Paths
A key path is a string
of dot-separated keys used to specify a sequence of object
properties to traverse. For example, the key path
owner.address.street
applied to a bank account instance refers to the value of the
street string that is stored in the address of the bank
account’s owner, assuming the
Person
and
Address
classes
are also key-value coding compliant.
In Swift, you can use a key-path expression to create key paths for accessing properties.
let balanceAccessor = \BankAccount.
currentBalance
myAccount.value(forKeyPath:balanceAccessor) //Type 1
myAccount.value(forKeyPath: #keyPath(BankAccount.currentBalance
)//Type 2
Note: In Swift, instead of using a string to indicate a key or key
path, you can use a
#keyPath
expression. This offers the advantage of a compile time check,
as described in the
Keys and Key Paths
Key-Value Observing (KVO)
Key-Value Observing (KVO) is built on top of KVC. It allows you to observe (i.e. watch) a KVC key path on an object to see when the value changes. For example, let’s write some code that watches to see if a person’s address changes. There are three methods of interest in the following code:
-
watchPersonForChangeOfAddress:
begins the observing
//this begins the observing
[p addObserver:self forKeyPath:@”address” options:0 context:KVO_CONTEXT_ADDRESS_CHANGED]; -
observeValueForKeyPath:ofObject:change:context:
is called every time there is a change in the value of the observed key path
//whenever an observed key path changes, this method will be called — (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context; {
} -
dealloc
stops the observing
//must stop observing everything before this object is //deallocated, otherwise it will cause crashes
[p removeObserver:self forKeyPath:@”address”]
To be observable, it has to inherit from
NSObject
and it
has to have
dynamic
properties. Being
dynamic
means that a property can be dynamically dispatched at runtime.
I have attached KVO demo video for reference, let’s say I have a
WKWebView
object and that I want to observe one of its properties,
estimatedProgress.
Bonus time: What’s new in Swift 4
1. String will be treated as collection- SE-0163
let message = "Message!"
message.count // no need of message.characters.count
for character in message { // no need of message.characters
print(character)
}
2. MutableCollection.swapAt-SE-0173
New swap function now takes indices of elements which are to be swapped.
var names = [“Vipul”,”Vinay”,”Vishal”,”Akshay”]
names.swapAt(2,3)
3. Dictionary and Set enhancements — SE-0165
a. Now you can create a dictionary using array elements.
b. Provide default value if value for key does not exist. In following statement if there is no value for key 10 found then it will print default value.
print(friendsDictionary[10, default: “No Friends Exist yet!”])
c. Partition array in different buckets
let mates = [“Akshay”, “Anil”, “Vishal”, “Vinay”, “Ankit”]
let buckets = Dictionary(grouping: mates, by: { $0.first! })
4. Swift Archival & Serialization — SE-0166
Good News! Swift 4 comes with built in encoders and decoders for JSON. JSON <-> Model conversion is comes built in ( Although you can customize that behavior if required ).
5. Smart KeyPaths: Key-Value Coding — SE-0161
This is one of my favorite feature in Swift 4. You can grab root object name and drill down upto any property name to get its value.
References:-
swift-evolution - This maintains proposals for changes and user-visible enhancements to the Swift Programming Language.github.com