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下载到

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值