简洁的KVO -- 使用Block响应事件

涉及内容:

KVO,Runtime,Category,Block

首先创建NSObject的Category

举个例子是这样的:

随后定义你需要响应的Block结构

我简单一点就这样咯

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

 

添加方法

- (void)setObserveKeyPath:(id)keypath signal:(NSString *)sign didChangedBlock:(NickyObserverBlock)block;

keypath就不解释了,singal是标识

标识存在的意义是 在多个observe中 对同一个对象监听时 作为区分作用

随后往NSObject里添加一个字典对象 用作存储 对象设置的block和keypath+signal

字典的key为 keypath+signal object为block

随后实现上面定义的setObserve方法

- (void)setObserveKeyPath:(id)keypath signal:(NSString *)sign didChangedBlock:(NickyObserverBlock)block{
    [self.changeBlockMaps setObject:block forKey:[NSString stringWithFormat:@"%@%p",keypath,&sign]];
    [self addObserver:self forKeyPath:keypath options:(NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew) context:&sign];
}

在实现方法 里,对象给自己添加KVO,同时设置keypath及context,context必须设置,不然后面在kvo响应里无法取到我们字典里存储的block

然后实现kvo事件:

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    NSString *path = [NSString stringWithFormat:@"%@%p",keyPath,context];
    NickyObserverBlock block = [self.changeBlockMaps objectForKey:path];
    if (block){
        block(change[@"old"],change[@"new"]);
    }
}

path为前面设置时拼接的keypath+signal

取到里面的block对象,并执行

 

到最后 请不要忘记对象销毁时手动把observe remove掉(虽然ios10之后不remove也不会崩...)..

完整的category:

 

.h

#import <Foundation/Foundation.h>
typedef void(^NickyObserverBlock)(id oldValue,id newValue);
@interface NSObject (NickyKVO)
- (void)setObserveKeyPath:(id)keypath didChangedBlock:(NickyObserverBlock)block;
- (void)setObserveKeyPath:(id)keypath signal:(NSString *)sign didChangedBlock:(NickyObserverBlock)block;

@end

 

.m

//
//  NSObject+NickyKVO.m
//  KvoDemo
//
//  Created by NickyTsui on 2018/3/30.
//  Copyright © 2018年 nickytsui. All rights reserved.
//

#import "NSObject+NickyKVO.h"
#import <objc/runtime.h>
@interface NSObject()
@property (strong,nonatomic) NSMutableDictionary <NSString *,NickyObserverBlock>                *changeBlockMaps;
@end

@implementation NSObject (NickyKVO)
- (void)setObserveKeyPath:(id)keypath signal:(NSString *)sign didChangedBlock:(NickyObserverBlock)block{
    [self.changeBlockMaps setObject:block forKey:[NSString stringWithFormat:@"%@%p",keypath,&sign]];
    [self addObserver:self forKeyPath:keypath options:(NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew) context:&sign];
}
- (void)setObserveKeyPath:(id)keypath didChangedBlock:(NickyObserverBlock)block{
    [self setObserveKeyPath:keypath signal:@"" didChangedBlock:block];
}
- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change context:(void *)context{
    NSString *path = [NSString stringWithFormat:@"%@%p",keyPath,context];
    NickyObserverBlock block = [self.changeBlockMaps objectForKey:path];
    if (block){
        block(change[@"old"],change[@"new"]);
    }
}

- (NSMutableDictionary<NSString *,NickyObserverBlock> *)changeBlockMaps{
    NSMutableDictionary<NSString *,NickyObserverBlock> * maps = objc_getAssociatedObject(self,_cmd);
    if (!maps){
        maps = [[NSMutableDictionary alloc]init];
        self.changeBlockMaps = maps;
    }
    return maps;
}
- (void)setChangeBlockMaps:(NSMutableDictionary *)changeBlockMaps{
    objc_setAssociatedObject(self, @selector(changeBlockMaps), changeBlockMaps, OBJC_ASSOCIATION_RETAIN_NONATOMIC);

}
@end

 

另外 这里涉及Category里创建一个property相关问题,可以看看博客前面的文章.

posted @ 2018-04-12 10:25  老徐想减肥  阅读(389)  评论(0编辑  收藏  举报