KVO usage Registration
[object addObserver:observer forKeyPath:@"text" options:NSKeyValueObservingOptionNew context:nil];
Implementation callback Method
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context{ if([keyPath isEqualToString:@"text"]) { NSLog(@"text:@%@", change[NSKeyValueChangeNewKey]); }}
Cancel registration upon release
[object removeObserver:self forKeyPath:@"text"];
Here are a few questions
When too many parameters are released during registration, the registration must be canceled and there is only one callback. when too many registered observers are registered, the code will become messy. KVO Encapsulation
Below we will encapsulate these issues
Define an observer class
@ Interface XYObserver: NSObject @ end @ interface XYObserver () @ property (nonatomic, assign) XYObserverType; // The type of the observer @ property (nonatomic, weak) id target; // The object of the response method after the value of the observed object changes @ property (nonatomic, assign) SEL selector; // response method after the observed object value changes @ property (nonatomic, copy) XYObserver_block_sourceObject_new_old block; // The block @ property (nonatomic, assign) executed when the value changes) id sourceObject; // The observed object @ property (nonatomic, strong) NSString * keyPath; // The observed object's keyPath-(instancetype) initWithSourceObject :( id) sourceObject keyPath :( NSString *) keyPath target :( id) target selector :( SEL) selector type :( XYObserverType) type;-(instancetype) initWithSourceObject :( id) sourceObject keyPath :( NSString *) keyPath block :( XYObserver_block_sourceObject_new_old) block; @ end
Add NSObject about observer category
@ Interface NSObject (XYObserver) @ property (nonatomic, readonly, strong) NSMutableDictionary * observers; /*** api parameters description ** the keyPath * target of the keypath observed object is self * selector @ selector (propertyNew :) @ selector (propertyNew: old :) @ selector (propertyIn: new: old :) * type is automatically assigned to * block selector Based on selector. Select either block or */-(void) observeWithObject :( id) sourceObject property :( NSString *) property;-(void) observeWithObject :( id) sourceObject property :( NSString *) property block :( XYObserver_block_sourceObject_new_old) block;-(void) removeObserverWithObject :( id) sourceObject property :( NSString *) property;-(void) removeObserverWithObject :( id) sourceObject;-(void) removeAllObserver; @ end
The implementation method of the query here
-(void) observeWithObject:(id)object property:(NSString*)property{ SEL aSel = NSSelectorFromString([NSString stringWithFormat:@"%@New:", property]); if ([self respondsToSelector:aSel]) { [self observeWithObject:object keyPath:property target:self selector:aSel type:XYObserverType_new]; return; } . . .}
Save the block directly.
-(void) observeWithObject:(id)object property:(NSString*)property block:(XYObserver_block_sourceObject_new_old)block{ [self observeWithObject:object keyPath:property block:block];}
Solution
-(void) observeValueForKeyPath:(NSString*)keyPath ofObject:(id)object change:(NSDictionary*)change context:(void*)context{ __weak __typeof(self) weakSelf = self; if (_block) { _block(weakSelf, change[NSKeyValueChangeNewKey], change[NSKeyValueChangeOldKey]); return; } if (_type == XYObserverType_new) { action(_target, _selector, change[NSKeyValueChangeNewKey]); }else if (_type == XYObserverType_new_old) { action(_target, _selector, change[NSKeyValueChangeNewKey], change[NSKeyValueChangeOldKey]); }else if (_type == XYObserverType_self_new) { action(_target, _selector, self, change[NSKeyValueChangeNewKey]); }else if (_type == XYObserverType_self_new_old) { action(_target, _selector, self, change[NSKeyValueChangeNewKey], change[NSKeyValueChangeOldKey]); }}
Add all the observers to a dictionary.
-(void) observeWithObject:(id)object keyPath:(NSString*)keyPath target:(id)target selector:(SEL)selector type:(XYObserverType)type{ XYObserver *ob = [[XYObserver alloc] initWithSourceObject:object keyPath:keyPath target:target selector:selector type:type]; NSString *key = [NSString stringWithFormat:@"%@_%@", object, keyPath]; [self.observers setObject:ob forKey:key];}-(void) observeWithObject:(id)object property:(NSString*)property block:(XYObserver_block_sourceObject_new_old)block{ [self observeWithObject:object keyPath:property block:block];}-(id) observers{ id object = objc_getAssociatedObject(self, NSObject_observers); if (nil == object) { NSMutableDictionary *dic = [NSMutableDictionary dictionaryWithCapacity:8]; objc_setAssociatedObject(self, NSObject_observers, dic, OBJC_ASSOCIATION_RETAIN_NONATOMIC); return dic; } return object;}
When an object is released, the observer object in the dictionary is cleared and the observer object is not registered in the dealloc method of the observer object.
-(void) dealloc{ if (_sourceObject) { [_sourceObject removeObserver:self forKeyPath:_keyPath]; }}
To facilitate writing, define several macros
#define ON_KVO_1_( __property ) -(void) __property##New:(id)newValue#define ON_KVO_2_( __property ) -(void) __property##New:(id)newValue old:(id)oldValue#define ON_KVO_3_( __property ) -(void) __property##In:(id)sourceObject new:(id)newValue#define ON_KVO_4_( __property ) -(void) __property##In:(id)sourceObject new:(id)newValue old:(id)oldValue
Demo used
[self observeWithObject:self property:@"testKVO"];ON_KVO_4_(testKVO){ NSLogD(@"obj:%@ new:%@ old:%@", sourceObject, newValue, oldValue);}[self observeWithObject:self property:@"testKVO2" block:^(id sourceObject, id newValue, id oldValue) { NSLogD(@"obj:%@ new:%@ old:%@", sourceObject, newValue, oldValue); }];
The advantage of this encapsulation is that you do not need to remember many things when using KVO.
The code can be downloaded at https://github.com/uxyheaven/xyquickdevelop