KVO简化版本

直接上简化后的KVO实现代码:

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        ZYObject *obj = [[ZYObject alloc]init];
        ZYObserver *observer = [[ZYObserver alloc]init];
        
        obj.name = @"value1";
        obj.age = 26;
        
        [obj addObserver:observer forKeyPath:KVOClassKeyPath(ZYObject,name) options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld usingBlock:^(NSString *keyPath, id object, NSDictionary *change) {
            NSLog(@"\nkeyPath:%@\nobject:%@\nchange:%@",keyPath,object,change);
        }];

        [obj addObserver:observer forKeyPath:KVOClassKeyPath(ZYObject,name) usingBlock:^(id newValue, id oldValue) {
            NSLog(@"\nnewValue:%@\noldValue:%@\n",newValue,oldValue);
        }];
        
        NSSet *set = [NSSet setWithArray:@[KVOClassKeyPath(ZYObject,name),KVOClassKeyPath(ZYObject,age)]];
        [obj addObserver:observer forKeyPathSet:set options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld usingBlock:^(NSString *keyPath, id object, NSDictionary *change) {
            NSLog(@"\nkeyPath:%@\nobject:%@\nchange:%@",keyPath,object,change);
        }];
        
        obj.name = @"value2";
        observer.name = @"value3";
        obj.age = 27;
    }
    return 0;
}

实现原理:

设计一个辅助类KVOController去管理Observer所有的观察实现,这个KVOController通过运行时添加为Observer的属性,在Observer调用dealloc之前,KVOController会先调用自己的dealloc方法,在这个方法中,移除Observer的所有观察对象.

上实现代码:
NSObject+KVOController.h

#import 
#import "KVOController.h"

#define KVOClassKeyPath(CLASS, KEYPATH) \
@((((CLASS *)(nil)).KEYPATH, #KEYPATH))

@interface NSObject (KVOController)

- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options usingBlock:(kvoCallBack)block;

- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath usingBlock:(kvoBriefCallBack)block;

- (void)addObserver:(NSObject *)observer forKeyPathSet:(NSSet *)keyPathSet options:(NSKeyValueObservingOptions)options usingBlock:(kvoCallBack)block;
@end

NSObject+KVOController.h

#import "NSObject+KVOController.h"
#import "KVOController.h"
#import 

@implementation NSObject (KVOController)

- (KVOController *)kvoController{
    KVOController *obj = objc_getAssociatedObject(self, @selector(kvoController));
    if (!obj) {
        obj = [[KVOController alloc]init];
        objc_setAssociatedObject(self, @selector(kvoController), obj, OBJC_ASSOCIATION_RETAIN);
    }
    return obj;
}

- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options usingBlock:(kvoCallBack)block{
    [observer.kvoController observe:self forKeyPath:keyPath options:options usingBlock:block];
}

- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath usingBlock:(kvoBriefCallBack)block{
    [self addObserver:observer forKeyPath:keyPath options:NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld usingBlock:^(NSString *keyPath, id object, NSDictionary *change) {
        
        id newValue = change[NSKeyValueChangeNewKey];
        id oldValue = change[NSKeyValueChangeOldKey];
        
        if ([newValue isKindOfClass:[NSNull class]]) {
            newValue = nil;
        }
        
        if ([oldValue isKindOfClass:[NSNull class]]) {
            oldValue = nil;
        }
        block(newValue,oldValue);
    }];
}

- (void)addObserver:(NSObject *)observer forKeyPathSet:(NSSet *)keyPathSet options:(NSKeyValueObservingOptions)options usingBlock:(kvoCallBack)block{
    for (NSString *keyPath in keyPathSet) {
        [self addObserver:observer forKeyPath:keyPath options:options usingBlock:block];
    }
}
@end

KVOController.h

#import 

typedef void (^kvoCallBack)(NSString * keyPath,id object,NSDictionary * change);

typedef void (^kvoBriefCallBack)(id newValue,id oldValue);

@interface KVOController : NSObject
- (void)observe:(NSObject *)beObserved forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options usingBlock:(kvoCallBack)block;

- (void)observe:(NSObject *)beObserved forKeyPathSet:(NSMutableSet *)keyPathSet options:(NSKeyValueObservingOptions)options usingBlock:(kvoCallBack)block;
@end

KVOController.m

#import "KVOController.h"

@interface KVOUnitInfo : NSObject
@property (nonatomic,copy) NSString *keyPath;
@property (nonatomic,strong) NSObject *beObserved;
@property (nonatomic,copy,readonly) kvoCallBack block;
- (instancetype)initWithBlock:(kvoCallBack)block;
@end

@implementation KVOUnitInfo
- (instancetype)initWithBlock:(kvoCallBack)block{
    self = [super init];
    if (self) {
        _block = [block copy];
    }
    return self;
}

- (void)observeValueForKeyPath:(NSString *)keyPath
                      ofObject:(id)object
                        change:(NSDictionary *)change
                       context:(void *)context{
    if (_block) {
        _block(keyPath,object,change);
    }
}
@end

@interface KVOController ()
@property (nonatomic,strong) NSMutableDictionary *keyPathObserversDic;
@end

@implementation KVOController

- (instancetype)init
{
    self = [super init];
    if (self) {
        _keyPathObserversDic = self.keyPathObserversDic;
    }
    return self;
}

- (NSMutableDictionary *)keyPathObserversDic{
    if (!_keyPathObserversDic) {
        _keyPathObserversDic = [NSMutableDictionary dictionary];
    }
    return _keyPathObserversDic;
}

- (void)observe:(NSObject *)beObserved forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options usingBlock:(kvoCallBack)block{
    NSAssert(keyPath, @"keyPath shoule not be nil");
    
    KVOUnitInfo *unit = [[KVOUnitInfo alloc]initWithBlock:block];
    unit.keyPath = keyPath;
    unit.beObserved = beObserved;
    
    @synchronized(self.keyPathObserversDic) {
        NSMutableArray *keyPathObservers = self.keyPathObserversDic[keyPath];
        if (!keyPathObservers) {
            keyPathObservers = [NSMutableArray array];
            self.keyPathObserversDic[keyPath] = keyPathObservers;
        }
        [keyPathObservers addObject:unit];
    }
    
    [beObserved addObserver:unit forKeyPath:keyPath options:options context:NULL];
}

- (void)observe:(NSObject *)beObserved forKeyPathSet:(NSMutableSet *)keyPathSet options:(NSKeyValueObservingOptions)options usingBlock:(kvoCallBack)block{
    for (NSString *keyPath in keyPathSet) {
        [self observe:beObserved forKeyPath:keyPath options:options usingBlock:block];
    }
}

- (void)_uninstallObserver{
    for (NSString *keyPath in self.keyPathObserversDic.allKeys) {
        NSArray *observers = self.keyPathObserversDic[keyPath];
        for (KVOUnitInfo *unit in observers) {
            [unit.beObserved removeObserver:unit forKeyPath:unit.keyPath];
        }
    }
}

- (void)dealloc{
    [self _uninstallObserver];
}
@end

参考资料:KVOController

你可能感兴趣的:(KVO简化版本)