#import <Foundation/Foundation.h>
typedef void(^ObserverBlock)(NSDictionary *change);
@interface KVOObserver : NSObject
/*block
*
*object:被观察者
*/
+ (id)observerForObject:(id)object
keyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
changeBlock:(ObserverBlock)block;
/*selector
*
*object:被观察者
*action:-(void)targetActionCallbackForObject:(id)object keyPath:(NSString*)keyPath change:(NSDictionary*)change
*/
+ (id)observerForObject:(id)object
keyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
target:(id)target
action:(SEL)action;
@end
#import "KVOObserver.h"
//加入头文件
#import <objc/message.h>
@interface KVOObserver ()
@property(nonatomic,copy)NSString *keyPath;
@property(nonatomic,copy)ObserverBlock block;
@property(nonatomic,__unsafe_unretained)id observedObject;
@end
@implementation KVOObserver
- (void)dealloc
{
if(_observedObject) {
[self stopObserving];
}
[super dealloc];
}
- (void)stopObserving
{
[_observedObject removeObserver:self forKeyPath:_keyPath];
_observedObject = nil;
self.block = nil;
self.keyPath = nil;
}
#pragma mark -- Block
+ (id)observerForObject:(id)object
keyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
changeBlock:(ObserverBlock)block
{
KVOObserver *observer = [[KVOObserver alloc]init];
if(observer)
{
if(!object || !keyPath || !block)
{
// 抛出异常
[NSException raise:NSInternalInconsistencyException format:@"Observation must have a valid object (%@), keyPath (%@) and block(%@)", object, keyPath, block];
self = nil;
} else
{
observer.observedObject = object;
observer.keyPath = keyPath ;
observer.block = block;
[observer.observedObject addObserver:observer
forKeyPath:observer.keyPath
options:options
context:NULL];
}
}
return observer;
}
- (void)observeValueForKeyPath:(NSString *)keyPath
ofObject:(id)object
change:(NSDictionary *)change
context:(void *)context
{
(self.block)(change);
}
#pragma mark -- Selector
+ (id)observerForObject:(id)object
keyPath:(NSString *)keyPath
options:(NSKeyValueObservingOptions)options
target:(id)target
action:(SEL)action
{
id observer = nil;
__weak id wTarget = target;
__weak id wObject = object;
ObserverBlock block = ^(NSDictionary *change) {
id msgTarget = wTarget;
if(msgTarget) {
((void(*)(id, SEL, id, NSString *, NSDictionary *))objc_msgSend)(msgTarget, action, wObject, keyPath, change);
}
};
if(block) {
observer = [KVOObserver observerForObject:object keyPath:keyPath options:options changeBlock:block];
}
return observer;
}
@end
应用举例:
@interface AppDelegate ()
@property (nonatomic,copy)NSString *propertyToObserve;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
self.propertyToObserve = [NSString stringWithFormat:@"old"];
AppDelegate* object = self;
KVOObserver *observerBlock = [KVOObserver observerForObject:object keyPath:@"propertyToObserve" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld changeBlock:^(NSDictionary *change) {
NSLog(@"propertyToObserve changed old:%@--new:%@",change[NSKeyValueChangeOldKey],change[NSKeyValueChangeNewKey]);
}];
KVOObserver *observerSelector = [KVOObserver observerForObject:object keyPath:@"propertyToObserve" options:NSKeyValueObservingOptionInitial | NSKeyValueObservingOptionNew| NSKeyValueObservingOptionOld target:self action:@selector(targetActionCallbackForObject:keyPath:change:)];
[self performSelector:@selector(changePropertyToObserveValue) withObject:nil afterDelay:5];
}
-(void)targetActionCallbackForObject:(id)object keyPath:(NSString*)keyPath change:(NSDictionary*)change
{
NSLog(@"change %@",change);
}
-(void)changePropertyToObserveValue
{
self.propertyToObserve = [NSString stringWithFormat:@"new"];
}
结果:
2015-01-21 19:00:10.701 Production[9112:935290] propertyToObserve changed old:(null)--new:old
2015-01-21 19:00:10.702 Production[9112:935290] change {
kind = 1;
new = old;
}
2015-01-21 19:00:15.704 Production[9112:935290] change {
kind = 1;
new = new;
old = old;
}
2015-01-21 19:00:15.704 Production[9112:935290] propertyToObserve changed old:old--new:new
参考:
http://joeyio.com/2013/10/21/lightweight_kvo/
https://github.com/th-in-gs/THObserversAndBinders