iOS

iOS-KVO(四) 自定义KVO+Block

2019-07-05  本文已影响0人  厦门_小灰灰

iOS-KVO(一) 基本操作
iOS-KVO(二) 使用注意点
iOS-KVO(三) 窥探底层实现
iOS-KVO(四) 自定义KVO+Block

这一节,将自定义KVO,并且通过Block进行返回属性的变化。只是验证大致流程,并未做一些判断,比如传入key是空之类的。

我们还是按照以下流程来讲代码:

KVO主要流程:

  1. 创建一个中间类并创建class方法,返回父类;
  2. isa指针指向中间类;
  3. 重写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方法

 @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方法

/**
 是否已经存在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;
}
    //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!

上一篇下一篇

猜你喜欢

热点阅读