KVO for IOS SDK
Preface: KVC and KVO are tools that help us navigate the dynamic features of objective C. KVO is based on KVC, so if you are not familiar with KVC, you can refer to my blog. Here I will not repeat KVC.
Content of this article
KVO Definition
Typical use cases of KVO.
Manual KVO
What KVO should do
Definition of a KVO
KVO provides a key-value-observing mechanism, that is, you can obtain value changes by listening to the key. Monitors status changes between objects. KVO classes follow the protocol. In fact, any class inherited from NSObject follows this protocol. In Object C, almost all classes are from NSObject.
KVO is generally divided into three steps.
1.1 subscribe to the keypath you want to listen
Functions
- (void)addObserver:(NSObject *)anObserver forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
Registration notification
Observer: the observer, that is, the subscriber of KVO notifications. Subscription is required
ObserveValueForKeyPath: ofObject: change: context: Method keyPath: Describes the attribute to be observed, relative to the observed attribute. Options: Some attributes of KVO are configured. There are four options. Context: context, which will be passed to the subscribed function to differentiate messages. Therefore, it should be different.
Options content
NSKeyValueObservingOptionNew: change dictionary includes changed values NSKeyValueObservingOptionOld: change dictionary includes changed values NSKeyValueObservingOptionInitial: KVO notification is triggered immediately after registration: whether to notify before the value is changed (this key determines whether to notify the change twice before the change)
1.2 respond to status changes
Every time the monitored keyPath changes, it will be called back in this function.
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
KeyPath: The monitored keyPath, which is used to distinguish different KVO listeners. Object: the modified object to be observed (the modified value can be obtained through the object) change: the dictionary for saving information changes (old values, new values, etc. may exist) context: context, it is used to differentiate different KVO listeners.
1.3 cancel subscription when appropriate
Two functions are usually used.
- (void)removeObserver:(NSObject *)anObserver forKeyPath:(NSString *)keyPath- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath context:(void *)context
This is usually written during the remove operation, because during the remove operation, you cannot determine whether the remove operation is successful.
@try { [object removeObserver:self forKeyPath:keyPath]; } @catch (NSException *exception) { NSLog(@%@,exception); }
Typical use cases of erkvo-synchronization between model and view
Here, you will see a complete KVO example. Like the previous KVC, I wrote a similar demo. Click "random" to randomly change the User's age, and the new and old age will be displayed synchronously on the UI.
The implementation process is as follows:
Defines a User class as a Model.
@interface User : NSObject@property (strong,nonatomic) NSString * name;@property (nonatomic) NSUInteger age;@end
Defines two static variables, one as the keyPath and the other as the context
static NSString * observename = @age;static void * privateContext = 0;
Then register (subscribe) KVO in viewWillAppear and delete KVO from viewWillDisappear.
-(void)viewWillAppear:(BOOL)animated{ [self.user addObserver:self forKeyPath:observename options:NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew context:privateContext];}-(void)viewWillDisappear:(BOOL)animated{ @try { [self.user removeObserver:self forKeyPath:observename]; } @catch (NSException *exception) { NSLog(@%@,exception); }}
When you click random, the age changes.
- (IBAction)random:(id)sender { self.user.age = arc4random()%100 +1;}
Then, synchronize the model and view in the function mentioned above.
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ if (context == privateContext) { if ([keyPath isEqualToString:observename]) { NSNumber * old = [change objectForKey:NSKeyValueChangeOldKey]; NSNumber * new = [change objectForKey:NSKeyValueChangeNewKey]; self.lastvalue.text = [NSString stringWithFormat:@%@,old]; self.newvalue.text = [NSString stringWithFormat:@%@,new]; } }}
Three manual KVO
The implementation of KVO is to automatically implement two functions in the registered keyPath, which are automatically called in Setter.
- (void)willChangeValueForKey:(NSString *)key- (void)didChangeValueForKey:(NSString *)key
Sometimes, we need to implement manual KVO
In this case, you need to disable the automatic generation of KVO notifications and then manually call them. The advantage of manual notifications is that you can flexibly Add the desired judgment conditions. For example
+(BOOL)automaticallyNotifiesObserversOfAge{ return NO;}-(void)setAge:(NSUInteger)age{ if (age < 22) { return; } [self willChangeValueForKey:@age]; _age = age; [self didChangeValueForKey:@age];}
KVO and Context
Since Context is usually used to distinguish different KVO, the uniqueness of context is very important. Generally, I use static variables in the current. m file.
static void * privateContext = 0;
KVO and thread
The KVO response and the value changes observed by KVO are on one thread. Therefore, do not mix KVO with multithreading in most cases. Unless it can ensure that all observers can handle KVO in a thread safe way
Value of a listener change
Before and after the change
id oldValue = change[NSKeyValueChangeOldKey];id newValue = change[NSKeyValueChangeNewKey];