美文网首页
KVO简化版本

KVO简化版本

作者: AryCode | 来源:发表于2017-10-23 15:03 被阅读16次

    直接上简化后的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<NSKeyValueChangeKey,id> *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<NSKeyValueChangeKey,id> *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 <Foundation/Foundation.h>
    #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 <NSString *>*)keyPathSet options:(NSKeyValueObservingOptions)options usingBlock:(kvoCallBack)block;
    @end
    

    NSObject+KVOController.h

    #import "NSObject+KVOController.h"
    #import "KVOController.h"
    #import <objc/runtime.h>
    
    @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<NSKeyValueChangeKey,id> *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 <NSString *>*)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 <Foundation/Foundation.h>
    
    typedef void (^kvoCallBack)(NSString * keyPath,id object,NSDictionary<NSKeyValueChangeKey,id> * 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 <NSString *>*)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<NSKeyValueChangeKey,id> *)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 <NSString *>*)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简化版本

          本文链接:https://www.haomeiwen.com/subject/kqcauxtx.html