博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
神奇的 BlocksKit(1):源码分析(下)
阅读量:5936 次
发布时间:2019-06-19

本文共 5259 字,大约阅读时间需要 17 分钟。

私有类 _BKObserver

 

_BKObserver 是用来观测属性的对象,它在接口中定义了 4 个属性:

 

@property (nonatomic,readonly,unsafe_unretained) id observee;

@property (nonatomic,readonly) NSMutableArray *keyPaths;

@property (nonatomic,readonly) id task;

@property (nonatomic,readonly) BKObserverContext context;

 

上面四个属性的具体作用在这里不说了,上面的 bk_addObserverForKeyPaths:identifier:options:context: 方法中调用_BKObserver 的初始化方法 initWithObservee:keyPaths:context:task: 太简单了也不说了。

 

_BKObserver *observer = [[_BKObserver alloc] initWithObservee:self keyPaths:keyPaths context:context task:task];

[observer startObservingWithOptions:options];

 

上面的第一行代码生成一个 observer 实例之后立刻调用了 startObservingWithOptions: 方法开始观测对应的 keyPath:

 

(void)startObservingWithOptions:(NSKeyValueObservingOptions)options

{

    @synchronized(self) {

        if (_isObserving) return;

 

        #1:遍历 keyPaths 实现 KVO

 

        _isObserving = YES;

    }

}

 

startObservingWithOptions: 方法最重要的就是第 #1 部分:

 

[self.keyPaths bk_each:^(NSString *keyPath) {

    [self.observee addObserver:self forKeyPath:keyPath options:options context:BKBlockObservationContext];

}];

 

遍历自己的 keyPaths 然后让 _BKObserver 作观察者观察自己,然后传入对应的 keyPath。

 

关于 _stopObservingLocked 方法的实现也十分的相似,这里就不说了。

 

[keyPaths bk_each:^(NSString *keyPath) {

    [observee removeObserver:self forKeyPath:keyPath context:BKBlockObservationContext];

}];

 

到目前为止,我们还没有看到实现 KVO 所必须的方法 observeValueForKeyPath:ofObject:change:context,这个方法就是每次属性改变之后的回调:

 

(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context

{

    if (context != BKBlockObservationContext) return;

 

    @synchronized(self) {

        switch (self.context) {

            case BKObserverContextKey{

                void (^task)(id) = self.task;

                task(object);

                break;

            }

            case BKObserverContextKeyWithChange{

                void (^task)(id,NSDictionary *) = self.task;

                task(object,change);

                break;

            }

            case BKObserverContextManyKeys{

                void (^task)(id,NSString *) = self.task;

                task(object,keyPath);

                break;

            }

            case BKObserverContextManyKeysWithChange{

                void (^task)(id,NSString *,NSDictionary *) = self.task;

                task(object,keyPath,change);

                break;

            }

        }

    }

}

 

这个方法的实现也很简单,根据传入的 context 值,对 task 类型转换,并传入具体的值。

 

这个模块倒着就介绍完了,在下一节会介绍 BlocksKit 对 UIKit 组件一些简单的改造。

 

改造 UIKit

 

在这个小结会具体介绍 BlocksKit 是如何对一些简单的控件进行改造的,本节大约有两部分内容:

 

  • UIGestureRecongizer + UIBarButtonItem + UIControl

  • UIView

 

改造 UIGestureRecongizer,UIBarButtonItem 和 UIControl

 

先来看一个 UITapGestureRecognizer 使用的例子

 

UITapGestureRecognizer *singleTap = [UITapGestureRecognizer bk_recognizerWithHandler:^(id sender) {

     NSLog(@"Single tap.");

} delay:0.18];

[self addGestureRecognizer:singleTap];

 

代码中的 bk_recognizerWithHandler:delay: 方法在最后都会调用初始化方法 bk_initWithHandler:delay: 生成一个UIGestureRecongizer 的实例

 

(instancetype)bk_initWithHandler:(void (^)(UIGestureRecognizer *sender,UIGestureRecognizerState state,CGPoint location))block delay:(NSTimeInterval)delay

{

    self = [self initWithTarget:self action:@selector(bk_handleAction:)];

    if (!self) return nil;

 

    self.bk_handler = block;

    self.bk_handlerDelay = delay;

 

    return self;

}

 

它会在这个方法中传入 target 和 selector。 其中 target 就是 self,而 selector 也会在这个分类中实现:

 

(void)bk_handleAction:(UIGestureRecognizer *)recognizer

{

    void (^handler)(UIGestureRecognizer *sender,UIGestureRecognizerState state,CGPoint location) = recognizer.bk_handler;

    if (!handler) return;

 

    NSTimeInterval delay = self.bk_handlerDelay;

 

    #1: 封装 block 并控制 block 是否可以执行

 

    self.bk_shouldHandleAction = YES;

 

    [NSObject bk_performAfterDelay:delay usingBlock:block];

}

 

因为在初始化方法 bk_initWithHandler:delay: 中保存了当前手势的 bk_handler,所以直接调用在 Block Execution 一节中提到过的 bk_performAfterDelay:usingBlock: 方法,将 block 派发到指定的队列中,最终完成对 block 的调用。

 

封装 block 并控制 block 是否可以执行

 

这部分代码和前面的部分有些相似,因为这里也用到了一个属性 bk_shouldHandleAction 来控制 block 是否会被执行:

 

CGPoint location = [self locationInView:self.view];

void (^block)(void) = ^{

    if (!self.bk_shouldHandleAction) return;

    handler(self,self.state,location);

};

 

同样 UIBarButtonItem 和 UIControl 也是用了几乎相同的机制,把 target 设置为 self,让后在分类的方法中调用指定的 block。

 

UIControlWrapper

 

稍微有些不同的是 UIControl。因为 UIControl 有多种 UIControlEvents,所以使用另一个类 BKControlWrapper 来封装handler 和 controlEvents

 

@property (nonatomic) UIControlEvents controlEvents;

@property (nonatomic,copy) void (^handler)(id sender);

 

其中 UIControlWrapper 对象以 {controlEvents,wrapper} 的形式作为 UIControl 的属性存入字典。

 

改造 UIView

 

因为在上面已经改造过了 UIGestureRecognizer,在这里改造 UIView 就变得很容易了:

 

(void)bk_whenTouches:(NSUInteger)numberOfTouches tapped:(NSUInteger)numberOfTaps handler:(void (^)(void))block

{

    if (!block) return;

 

    UITapGestureRecognizer *gesture = [UITapGestureRecognizer bk_recognizerWithHandler:^(UIGestureRecognizer *sender,UIGestureRecognizerState state,CGPoint location) {

        if (state == UIGestureRecognizerStateRecognized) block();

    }];

 

    gesture.numberOfTouchesRequired = numberOfTouches;

    gesture.numberOfTapsRequired = numberOfTaps;

 

    [self.gestureRecognizers enumerateObjectsUsingBlock:^(id obj,NSUInteger idx,BOOL *stop) {

        if (![obj isKindOfClass:[UITapGestureRecognizer class]]) return;

 

        UITapGestureRecognizer *tap = obj;

        BOOL rightTouches = (tap.numberOfTouchesRequired == numberOfTouches);

        BOOL rightTaps = (tap.numberOfTapsRequired == numberOfTaps);

        if (rightTouches && rightTaps) {

            [gesture requireGestureRecognizerToFail:tap];

        }

    }];

 

    [self addGestureRecognizer:gesture];

}

 

UIView 分类只有这一个核心方法,其它的方法都是向这个方法传入不同的参数,这里需要注意的就是。它会遍历所有的gestureRecognizers,然后把对所有有冲突的手势调用 requireGestureRecognizerToFail: 方法,保证添加的手势能够正常的执行。

 

转载地址:http://qzjtx.baihongyu.com/

你可能感兴趣的文章
深入理解Java中的逃逸分析
查看>>
CSS(溢出_判断IE版本)
查看>>
Thrift协议的服务模型
查看>>
豪掷 30 亿,支付宝能否“买”来刷脸支付的未来? ...
查看>>
Spring Cloud Alibaba基础教程:Nacos配置的多环境管理 ...
查看>>
Win10-MySQL-zip安装方法
查看>>
HashMap(JDK1.8)源码阅读记录
查看>>
windows安装python虚拟环境
查看>>
云上高性能计算--EHPC实现药物筛选最佳实践
查看>>
JavaScript常用数组操作方法,包含ES6方法
查看>>
车联网企业上海博泰获数亿元融资,苏宁领投
查看>>
【Util】 时间天数增加,时间比较。
查看>>
三种分布式爬虫系统的架构方式
查看>>
JVM基础面试题及原理讲解
查看>>
Chrome 70-71 Live Expression 及 global variable 功能
查看>>
从Python安装到语法基础,这才是初学者都能懂的爬虫教程
查看>>
span内文字居中css布局方法_让span内容居中
查看>>
Springboot自定义异常处理
查看>>
Android Camera2 预览功能实现
查看>>
python实现选择排序算法
查看>>