• iOS的KVO使用和轻量级封装


    KVO的使用方法

    • 注冊
    [object addObserver:observer forKeyPath:@"text" options:NSKeyValueObservingOptionNew context:nil];
    
    • 实现回调方法
    -(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
    {
        if([keyPath isEqualToString:@"text"])
        {
            NSLog(@"text:@%@", change[NSKeyValueChangeNewKey]);
        }
    }
    
    • 释放的时候取消注冊
    [object removeObserver:self forKeyPath:@"text"];
    

    这里有几个问题

    1. 注冊的时候參数过多
    2. 释放的时候必须取消注冊
    3. 仅仅有一个回调,当注冊的观察者过多的时候,会使得代码变得杂乱

    KVO的封装

    以下我们将针对这几个问题进行封装

    • 定义一个观察者类
    @interface XYObserver : NSObject
    @end
    
    
    @interface XYObserver ()
    
    @property (nonatomic, assign) XYObserverType type;      // 观察者的类型
    
    @property (nonatomic, weak) id target;                  // 被观察的对象的值改变时后的响应方法所在的对象
    @property (nonatomic, assign) SEL selector;             // 被观察的对象的值改变时后的响应方法
    @property (nonatomic, copy) XYObserver_block_sourceObject_new_old block;        // 值改变时运行的block
    
    @property (nonatomic, assign) id  sourceObject;         // 被观察的对象
    @property (nonatomic, strong) NSString *keyPath;        // 被观察的对象的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
    
    • 加入NSObject关于观察者的类别
    @interface NSObject (XYObserver)
    
    @property (nonatomic, readonly, strong) NSMutableDictionary *observers;
    
    /**
     * api parameters 说明
     *
     * sourceObject 被观察的对象
    
     * keyPath 被观察的属性keypath
    
     * target 默认是self
    
     * selector @selector(propertyNew:)
                @selector(propertyNew:old:)
                @selector(propertyIn:new:)
                @selector(propertyIn:new:old:)
    
     * type 依据selector自己主动赋值
    
     * block selector, block二选一
     */
    -(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
    
    • 在这里我们查询的实现的方法
    -(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;
        }
        .
        .
        .
    }
    
    • 用block的话就直接保存
    -(void) observeWithObject:(id)object property:(NSString*)property block:(XYObserver_block_sourceObject_new_old)block{
        [self observeWithObject:object keyPath:property block:block];
    }
    
    • 处理实现方法
    -(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]);
        }
    }
    
    • 把全部的观察者加入到一个字典里
    -(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;
    }
    • 当对象释放的时候会清空字典里的观察者对象,在观察者对象的dealloc方法里面取消注冊观察者
    -(void) dealloc
    {
        if (_sourceObject) { [_sourceObject removeObserver:self forKeyPath:_keyPath]; }
    }
    
    • 为了方便书写,定义几个宏
    #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

    [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);
        }];
    

    这个封装的长处是在使用KVO的时候不须要记住太多东西..

    代码能够在https://github.com/uxyheaven/XYQuickDevelop下载到

  • 相关阅读:
    html5的跨域处理
    file表单提交异步模拟
    Bigpipe :bigpipe的了解之2
    javascript获取指定父元素
    javascript淡入淡出的效果轮换转播
    IE下,动态创建的iframe在异步提交时会跳转的问题的解决
    javascriptIE不支持table的innerHTML解决方案
    javascript删除元素所引起的 对于NodeList的理解
    javascript淡入淡出的效果轮换转播后续
    数据加载的bigpipe
  • 原文地址:https://www.cnblogs.com/mqxnongmin/p/10593143.html
Copyright © 2020-2023  润新知