简洁的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相关问题,可以看看博客前面的文章.