iOS-KVO(四) 自定义KVO+Block
2019-07-05 本文已影响0人
厦门_小灰灰
iOS-KVO(一) 基本操作
iOS-KVO(二) 使用注意点
iOS-KVO(三) 窥探底层实现
iOS-KVO(四) 自定义KVO+Block
这一节,将自定义KVO,并且通过Block进行返回属性的变化。只是验证大致流程,并未做一些判断,比如传入key是空之类的。
我们还是按照以下流程来讲代码:
KVO主要流程:
- 创建一个中间类并创建class方法,返回父类;
- isa指针指向中间类;
- 重写setter方法;
另外自己另外写了一个类(LHObserver),来存放观察者,观察的key,以及block。
以下的代码全部都在一个类别中实现:
NSObject+BlockKVO
先简单看下 LHObserver这个类
@interface LHObserver : NSObject
@property (nonatomic, weak) NSObject *observer;
@property (nonatomic, copy) NSString *observerKey;
@property (nonatomic, weak) id context;
@property (nonatomic, copy) LH_ObserverHandler observerHandler;
@end
@implementation LHObserver
- (instancetype)initWithObersver:(NSObject *)observer
observerKey:(NSString *)observerKey
observerHandler:(LH_ObserverHandler)observerHandler
context:(nullable id)context
{
self = [super init];
if ( self ) {
self.observer = observer;
self.observerKey = observerKey;
self.observerHandler = observerHandler;
self.context = context;
}
return self;
}
@end
其中LH_ObserverHandler这个block为:
/**
监听属性变化
@param observerObject 观察者
@param observerKey 观察的名称
@param object 被观察者
@param change 监听返回的信息
@param context 上下文
*/
typedef void(^LH_ObserverHandler)(id observerObject, NSString *observerKey, id object, NSDictionary<NSKeyValueChangeKey,id> *change, id context);
接下来开始KVO的流程代码
创建一个中间类
- 拼接setter方法
/**
获取setter方法
@param observerKey 观察的名称
@return setter SEL
*/
- (SEL)setterSELWithObserverKey:(NSString *)observerKey
{
//比如observerKey的值为name,那么setter的方法名称就是 setName: 冒号不要忘记咯~
return NSSelectorFromString([NSString stringWithFormat:@"set%@:", [observerKey capitalizedString]]);
}
- 创建中间类
/**
创建中间类
@param originalClass 原先的类
@return 中间类
*/
- (Class)createKVOClassWithOriginalClass:(Class)originalClass
{
//获取原先类的名称
NSString *originalClassName = [NSString stringWithUTF8String:object_getClassName(originalClass)];
//拼接中间类的名称
NSString *kvoClassName = [NSString stringWithFormat:@"%@%@", KVO_CLASS_NAME_PREFIX, originalClassName];
//判断中间类是否存在,如果存在直接返回即可
Class kvoClass = objc_getClass(kvoClassName.UTF8String);
if ( kvoClass ) {
return kvoClass;
}
//不存在的话,就开始初始化与注册类
kvoClass = objc_allocateClassPair([self class], kvoClassName.UTF8String, 0);
objc_registerClassPair(kvoClass);
//添加class的方法,返回父类
Method classMethod = class_getInstanceMethod(originalClass, @selector(class));
class_addMethod(kvoClass, NSSelectorFromString(@"class"), (IMP)getKVOClass, method_getTypeEncoding(classMethod));
NSLog(@"%d", [self hasSelectorWithClass:kvoClass sel:@selector(class)]);
return kvoClass;
}
/**
返回父类
@param self self
@param _cmd _cmd
@return 父类
*/
Class getKVOClass(id self, SEL _cmd)
{
return class_getSuperclass(object_getClass(self));
}
isa指针指向中间类
//1.创建一个中间类, 2.并且isa指针指向中间类;
Class kvoClass = [self createKVOClassWithOriginalClass:[self class]];
//2.isa指针指向中间类;
object_setClass(self, kvoClass);
重写setter方法
- 先判断当前类是否已经存在setter方法了,如果有的话就不在添加
/**
是否已经存在sel方法
@param cls 类
@param sel sel方法
@return YES:存在 NO:不存在
*/
-(BOOL)hasSelectorWithClass:(Class)cls sel:(SEL)sel
{
unsigned int outCount = 0;
Method *methodList = class_copyMethodList(cls, &outCount);
for (int i = 0; i < outCount; ++i) {
Method method = methodList[i];
SEL selector = method_getName(method);
if ( selector == sel ) {
free(methodList);
return YES;
}
}
free(methodList);
return NO;
}
- setter方法的实现
//3.重写setter方法;
//(1)获取setter方法 - 上面已经先获取了
//(2)判断中间类是否存在setter方法
if ( ![self hasSelectorWithClass:kvoClass sel:setter] ) {
//(3)不存在就添加setter方法
class_addMethod(kvoClass, setter, (IMP)setterIMP, method_getTypeEncoding(setterMethod));
}
//存储LHObserver的对象的可变数组
NSMutableArray *observers = objc_getAssociatedObject(self, &LHOBSERVER_ARRAY_ASSOCIATED_KEY);
if ( !observers ) {
observers = [NSMutableArray array];
}
//初始化LHObserver的实例对象
LHObserver *observerInfo = [[LHObserver alloc] initWithObersver:observer
observerKey:observerKey
observerHandler:observerHandler
context:context];
[observers addObject:observerInfo];
objc_setAssociatedObject(self, &LHOBSERVER_ARRAY_ASSOCIATED_KEY, observers, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
void setterIMP(id self, SEL _cmd, id newValue)
{
/*
1.willChangeValueForKey
2.[super setter]
3.didChangeValueForKey
*/
//获取getter方法名称
NSString *getterFuncName = objc_getAssociatedObject(self, &LH_GETTER_FUNCTION_NAME);
//先将当前中间类保存下来
Class kvoClass = object_getClass(self);
//先将isa指针指向父类,注意之前已经实现过class方法
Class cls = [self class];
object_setClass(self, cls);
//获取旧数据
id oldValue = ((id(*)(id, SEL))objc_msgSend)(self, NSSelectorFromString(getterFuncName));
//调用父类的setter
((id(*)(id, SEL, id))objc_msgSend)(self, _cmd, newValue);
//拼凑数据
NSMutableDictionary *change = [@{} mutableCopy];
//这里要根据自己的需求来定,如果对应的value是空的话,直接赋值空字符串
if ( oldValue ) {
change[NSKeyValueChangeOldKey] = oldValue;
} else {
change[NSKeyValueChangeOldKey] = @"";
}
if ( newValue ) {
change[NSKeyValueChangeNewKey] = newValue;
} else {
change[NSKeyValueChangeNewKey] = @"";
}
//再将isa指针指向中间类
object_setClass(self, kvoClass);
NSMutableArray *observers = objc_getAssociatedObject(self, &LHOBSERVER_ARRAY_ASSOCIATED_KEY);
if ( !observers || observers.count <= 0 ) {
return;
}
//循环获取observerObject,然后执行block
for (LHObserver *observerObject in observers) {
if ( [observerObject.observerKey isEqualToString:getterFuncName] ) {
if ( observerObject.observerHandler ) {
dispatch_async(dispatch_get_global_queue(0, 0), ^{
//这里是在子线程
observerObject.observerHandler(observerObject.observer, observerObject.observerKey, self, change, observerObject.context);
});
}
break;
}
}
}
移除观察者
/**
移除观察者
@param observer 观察者
@param observerKey 观察的名称
*/
- (void)lh_removeObserver:(NSObject *)observer forObserverKey:(NSString *)observerKey
{
NSMutableArray *observers = objc_getAssociatedObject(self, &LHOBSERVER_ARRAY_ASSOCIATED_KEY);
if ( !observers ) {
return;
}
LHObserver *tempObserverObject = nil;
for (LHObserver *observerObject in observers) {
if ( observerObject.observer == observer && [observerObject.observerKey isEqualToString:observerKey] ) {
tempObserverObject = observerObject;
break;
}
}
//移除
if ( tempObserverObject ) {
[observers removeObject:tempObserverObject];
}
}
简单使用
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
@end
NS_ASSUME_NONNULL_END
#import "ViewController.h"
#import "Person.h"
#import "NSObject+BlockKVO.h"
@interface ViewController ()
@property (nonatomic, strong) Person *p1, *p2;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.p1 = [Person new];
[self.p1 setName:@"1"];
[self.p1 lh_addObserver:self
forObserverKey:@"name"
options:NSKeyValueObservingOptionNew|NSKeyValueObservingOptionOld
context:@"123"
observerHandler:^(id _Nonnull observerObject, NSString * _Nonnull observerKey, id _Nonnull object, NSDictionary<NSKeyValueChangeKey,id> * _Nonnull change, id _Nonnull context) {
NSLog(@"p1 %@, context:%@", change, context);
}];
[self.p1 setName:@"3"];
[self.p1 lh_removeObserver:self forObserverKey:@"name"];
}
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
[self.p1 setName:@"hui"];
}
- (void)dealloc
{
NSLog(@"%@ dealloc", [self class]);
}
@end
打印结果:
2019-07-05 23:52:53.765 KVODemo[5158:175811] {
new = 3;
old = 1;
}, context:123
这章主要都是代码,将之前说的底层原理的流程,自己通过代码简单的实现,代码还有很多可以改进的地方。
上面代码 传送门
KVO over!