私有类 _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: 方法,保证添加的手势能够正常的执行。

神奇的 BlocksKit(1):源码分析(下)的更多相关文章

  1. 知识小罐头07(tomcat8请求源码分析 下)

    感觉最近想偷懒了,哎,强迫自己也要写点东西,偷懒可是会上瘾的,嘿嘿!一有写博客的想法要赶紧行动起来,养成良好的习惯. ok,继续上一篇所说的一些东西,上一篇说到Connector包装了那两个对象,最后 ...

  2. 知识小罐头09(tomcat8启动源码分析 下)

    初始化已经完成,现在就是启动这些组件,Tomcat中的start方法就是用于启动的,其实start的原理还是和上一篇说的初始化几乎一样!这里我就大概说一下,看几个比较关键的地方就行了. 前面的步骤就大 ...

  3. memcached学习笔记——存储命令源码分析上篇

    原创文章,转载请标明,谢谢. 上一篇分析过memcached的连接模型,了解memcached是如何高效处理客户端连接,这一篇分析memcached源码中的process_update_command ...

  4. 内核通信之Netlink源码分析-用户内核通信原理

    2017-07-05 本节从一个小案例入手,结合源码分析下通过netlink进行内核和用户通信的流程. 内核端 按照传统CS模式,其实内核端可以作为是服务器端,用以接收用户的请求并作出处理,但是从ne ...

  5. illuminate/routing 源码分析之注册路由

    我们知道,在 Laravel 世界里,外界传进来一个 Request 时,会被 Kernel 处理并返回给外界一个 Response.Kernel 在处理 Request 时,会调用 illumina ...

  6. commons-logging + log4j源码分析

    分析之前先理清楚几个概念 Log4J = Log For Java SLF4J = Simple Logging Facade for Java 看到Facade首先想到的就是设计模式中的门面(Fac ...

  7. Windows平台下源码分析工具

    最近这段时间在阅读 RTKLIB的源代码,目前是将 pntpos.c文件的部分看完了,准备写一份文档记录下这些代码的用处.处理过程.理论公式来源.注意事项,自己还没有弄明白的地方.目前的想法是把每一个 ...

  8. SURF算法与源码分析、下

    上一篇文章 SURF算法与源码分析.上 中主要分析的是SURF特征点定位的算法原理与相关OpenCV中的源码分析,这篇文章接着上篇文章对已经定位到的SURF特征点进行特征描述.这一步至关重要,这是SU ...

  9. STL 源码分析《1》---- list 归并排序的 迭代版本, 神奇的 STL list sort

    最近在看 侯捷的 STL源码分析,发现了以下的这个list 排序算法,乍眼看去,实在难以看出它是归并排序. 平常大家写归并排序,通常写的是 递归版本..为了效率的考虑,STL库 给出了如下的 归并排序 ...

  10. QTimer源码分析(以Windows下实现为例)

    QTimer源码分析(以Windows下实现为例) 分类: Qt2011-04-13 21:32 5026人阅读 评论(0) 收藏 举报 windowstimerqtoptimizationcallb ...

随机推荐

  1. 【HDOJ】1297 Children’s Queue

    递推,最近发现自己做递推的题总是没有思路.下周多练习.对于f(n)可以在第n个位置为男生,此时共有f(n-1)种情况:若在第n个位置为女生,因此第n-1个位置也必须为女生.此时有两种情况,一种情况是在 ...

  2. Dom4j的使用(全而好的文章)

        版权声明: 本文由冰云完成,首发于CSDN,未经许可,不得使用于任何商业用途. 文中代码部分引用自DOM4J文档. 欢迎转载,但请保持文章及版权声明完整. 如需联络请发邮件:icecloud( ...

  3. c#调用c++动态库的一些理解

    调用c++动态库一般我们这样写   [DllImport("UCamer.dll", CallingConvention = CallingConvention.Winapi)] ...

  4. [swustoj 443] Handsome Swap

    Handsome Swap(0443) Time limit(ms): 1000 Memory limit(kb): 65535 Submission: 89 Accepted: 20 Accepte ...

  5. keychain中我的证书与证书, p12与pem, apns, 推送

    如果在A电脑上生成 的apns, 到B电脑上导入此文件时, 在 "我的证书"中找不到, 要去"证书"才能找到, 因为 创建时的csr不是本机生成的. 那这样的话 ...

  6. C# 根据Word模版生成Word文件

    指定的word模版 2,生成word类 添加com Microsoft word 11.0 Object Library 引用 using System; using System.Collectio ...

  7. HDU-5414 CRB and String

    http://acm.hdu.edu.cn/showproblem.php?pid=5414 题意:给定字符串s和t,可以在s里面选一个字符c,然后任选一个字符d(d!=c)将d插入到c的后面,问能不 ...

  8. 解决Mac下Sequel Pro 1.1 连接 Homebrew安装Mysql5.7.8的问题 Sequel Pro 1.1 encountered an unexpected error

    解决Mac下Sequel Pro 1.1 连接 Homebrew安装Mysql5.7.8的问题 Sequel Pro encountered an unexpected error Sequel Pr ...

  9. 【HTML】Beginner9:Form

    1.Form     Collect data inputted by a user.     Used as an interface for a web application,or send d ...

  10. oracle之spool详细使用总结

    今天实际项目中用到了spool,发现网上好多内容不是很全,自己摸索了好半天,现在总结一下. 一.通过spool 命令,可以将select 数据库的内容写到文件中,通过在sqlplus设置一些参数,使得 ...