BlocksKit(1)-基本类型的分类

BlocksKit是一个用block的方式来解决我们日常用对集合对象遍历、对象快速生成使用、block作为委托对象的一种综合便利封装库。这个库主要分三个大块Core、DynamicDelegate、UIKit三块内容,先分析一下第一部分Core,这个部门里面都是对基本类型的提供的分类接口,能够提供使用的便利。

集合类型提供的分类

代码提供了数组、字典、集合等类型的封装,以最常用的NSMutableArray+BlockKit来作为事例看一下:

- (void)bk_performSelect:(BOOL (^)(id obj))block;
- (void)bk_performReject:(BOOL (^)(id obj))block;
- (void)bk_performMap:(id (^)(id obj))block;

这些个方法,都是利用enumerateObjectsUsingBlock或者indexesOfObjectsPassingTest这些个方法去把block放到里面去进行遍历,然后决定计算出那些值或者是返回那些路径,在整个集合部分的话大概都是这种思路


便利的AssociatedObjects

在"NSObject+BKAssociatedObjects.h文件中提供了,便利的绑定方法,我们可以提供一个value和一个key直接就可以在NSObject中绑定属性,省去自己去编写objc_setAssociatedObject和 objc_getAssociatedObject(self, key)的不便利,这里面感觉虽然是放在BlockKit里面其实感觉并无和BlockKit关系不是很大,毕竟NSObject是非常常用的。另外,提供了不同关键字类型的绑定,亮点是实现了weak类型的属性的绑定,这里并没有使用OBJC_ASSOCIATION_ASSIGN这个关键字去绑定,而是把这个weak的value去绑定到一个OBJC_ASSOCIATION_RETAIN_NONATOMIC的对象上。避免使用了OBJC_ASSOCIATION_ASSIGN对使用的时候weak值的不确定性,原因移步(http://www.cocoachina.com/ios/20150629/12299.html),所以作者这个种做法非常的聪明。看一下代码:

- (void)bk_weaklyAssociateValue:(__autoreleasing id)value withKey:(const void *)key
{
_BKWeakAssociatedObject *assoc = objc_getAssociatedObject(self, key);
if (!assoc) {
assoc = [_BKWeakAssociatedObject new];
[self bk_associateValue:assoc withKey:key];
}
assoc.value = value;
} - (id)bk_associatedValueForKey:(const void *)key
{
id value = objc_getAssociatedObject(self, key);
if (value && [value isKindOfClass:[_BKWeakAssociatedObject class]]) {
return [(_BKWeakAssociatedObject *)value value];
}
return value;
}

_BKWeakAssociatedObject就是weak对象寄存的对象。


便利的block操作

在开发的时候,block需要不同的执行的情况不同执行。这里为我们提供一些便利的方案,比如,指定block在某个线程上延迟多少时间执行或者在后台执行这些场景。看一下这部分实现的核心的代码:

- (id)bk_performBlock:(void (^)(id obj))block onQueue:(dispatch_queue_t)queue afterDelay:(NSTimeInterval)delay
{
NSParameterAssert(block != nil); __block BOOL cancelled = NO; void (^wrapper)(BOOL) = ^(BOOL cancel) {
if (cancel) {
cancelled = YES;
return;
}
if (!cancelled) block(self);
}; dispatch_after(BKTimeDelay(delay), queue, ^{
wrapper(NO);
}); return [wrapper copy];
}

代码很简单,用了dispatch_after来处理接口传入的数据,然后返回一个叫wrapper的block,这点很好,这个任务能不能被取消就靠它了。一般在使用的时候我们都会保存这个返回的block,当需要取消这个操作的时候,那么就需要调用:

+ (void)bk_cancelBlock:(id)block
{
NSParameterAssert(block != nil);
void (^wrapper)(BOOL) = block;
wrapper(YES);
}

这个时候传入进去了一个yes,导致在执行的时候,直接就返回,block里面内容就不会被执行,从而达到取消的情况。


Block版的KVO

在日常的操作中,使用KVO的地方还是很多,但是KVO使用起来,苹果原生的API并没有那么便利,在这里面就提供了另外一种方式的封装,然我们作为开发者能够方便的使用KVO。

看一下最简单的添加观察的接口:

- (NSString *)bk_addObserverForKeyPath:(NSString *)keyPath task:(void (^)(id target))task
{
NSString *token = [[NSProcessInfo processInfo] globallyUniqueString];
[self bk_addObserverForKeyPaths:@[ keyPath ] identifier:token options:0 context:BKObserverContextKey task:task];
return token;
}

就是传一个keypath和一个block实现绑定的功能,这里面生成token的方法很有意思,生成一个唯一的字符串,作为这一次观察的标记。后面用这个标记加上keypath还可以移除这个观察。

看一下具体的实现代码:

- (void)bk_addObserverForKeyPaths:(NSArray *)keyPaths identifier:(NSString *)identifier options:(NSKeyValueObservingOptions)options context:(BKObserverContext)context task:(id)task
{
NSParameterAssert(keyPaths.count);
NSParameterAssert(identifier.length);
NSParameterAssert(task);
// 第一部分
Class classToSwizzle = self.class;
NSMutableSet *classes = self.class.bk_observedClassesHash;
@synchronized (classes) {
NSString *className = NSStringFromClass(classToSwizzle);
if (![classes containsObject:className]) {
SEL deallocSelector = sel_registerName("dealloc"); __block void (*originalDealloc)(__unsafe_unretained id, SEL) = NULL; id newDealloc = ^(__unsafe_unretained id objSelf) {
[objSelf bk_removeAllBlockObservers]; if (originalDealloc == NULL) {
struct objc_super superInfo = {
.receiver = objSelf,
.super_class = class_getSuperclass(classToSwizzle)
}; void (*msgSend)(struct objc_super *, SEL) = (__typeof__(msgSend))objc_msgSendSuper;
msgSend(&superInfo, deallocSelector);
} else {
originalDealloc(objSelf, deallocSelector);
}
}; IMP newDeallocIMP = imp_implementationWithBlock(newDealloc); if (!class_addMethod(classToSwizzle, deallocSelector, newDeallocIMP, "v@:")) {
// The class already contains a method implementation.
Method deallocMethod = class_getInstanceMethod(classToSwizzle, deallocSelector); // We need to store original implementation before setting new implementation
// in case method is called at the time of setting.
originalDealloc = (void(*)(__unsafe_unretained id, SEL))method_getImplementation(deallocMethod); // We need to store original implementation again, in case it just changed.
originalDealloc = (void(*)(__unsafe_unretained id, SEL))method_setImplementation(deallocMethod, newDeallocIMP);
} [classes addObject:className];
}
}
// 第二部分
NSMutableDictionary *dict;
_BKObserver *observer = [[_BKObserver alloc] initWithObservee:self keyPaths:keyPaths context:context task:task];
[observer startObservingWithOptions:options]; @synchronized (self) {
dict = [self bk_observerBlocks]; if (dict == nil) {
dict = [NSMutableDictionary dictionary];
[self bk_setObserverBlocks:dict];
}
} dict[identifier] = observer;
}

这个代码主要分成两个部分:

一个部分是如何去把观察的类的dealloc去swizzle掉,为要这样做呢,就是为了让添加的observer去执行一个remove的操作,去掉之前观察的那些path,防止由于使用者没有移除而导致的崩溃。如果做过这样的处理,那么就会把这个类的名字记录到self.class.bk_observedClassesHash这个NSMutableSet里面去。用于控制不至于把方法替换多遍。

第二部分是如何添加一个_BKObserver对象,这个对象就是一个具体的实例,用来持有我们需要的观察对象,并具体的进行观察的动作,这里要特别注意的是在_BKObserver的对象里面self.observee到底是谁。这个_BKObserver会被通过AssociatedObject方法去绑定到对象上,如果想要彻底的移除,一方面是要移除_BKObserver里面观察的那些个keypath的对象,另一方面是吧_BKObserver从对象绑定的字典里面移除去。

那么看一下_BKObserver的如何实现对keypath的观察:

- (void)startObservingWithOptions:(NSKeyValueObservingOptions)options
{
@synchronized(self) {
if (_isObserving) return; [self.keyPaths bk_each:^(NSString *keyPath) {
[self.observee addObserver:self forKeyPath:keyPath options:options context:BKBlockObservationContext];
}]; _isObserving = YES;
}
}

这里面self.observee就是我们现在操作观察的对象本身,然后呢self就是上面说的生成的_BKObserver(这个对象属性是unsafe_unretained),接着keyPath就是那些传进去的值,这里就是真正添加观察的地方了。然后就,值变化,再接着就回调传进来的block了。最后移除的流程就很简单了,按照上面说的移除流程,就可以了。

到这里,关于Core部门的代码就差不多都看了,很多的时候,这种通过分类去解决问题的思路是需要好好的体会的。通过绑定属性作为中间值,完成一些复杂的操作,按照这种思路都能在开发的过程中解决很多的稍微复杂而且重复的工作。

BlocksKit(1)-基本类型的分类的更多相关文章

  1. usb接口类型 简单分类辨识

    usb接口类型 简单分类辨识 - [相似百科] 庆欣 0.0 4 人赞同了该文章 1. 先放图,随着越来越多的接触智能设备,会遇到各种各样的usb接口,对于很多人来说,接口类型只有:usb接口,安卓接 ...

  2. ECMAScript---数据类型的分类

    数据值是一门编程语言生产的材料,JS中包含的值有以下类型: 1.基本数据类型(值类型):包含 数字 number.字符串string .布尔 boolean .null(其他语言都有的类型) .und ...

  3. Python标准类型的分类

    Python有3种不同的模型可以帮助对基本类型进行分类,这些类型更好的理解类型之间的相互关系以及他们的工作原理. 1 存储模型    能保存单个字面对象的类型,称为原子或标量存储:    能保存多个对 ...

  4. 112_Power Pivot 销售订单按 sku 订单类型特殊分类及占比相关

    博客:www.jiaopengzi.com 焦棚子的文章目录 请点击下载附件 一.背景 经过了一个双十一后,天天面对的都是订单.于是有了关于销售订单按sku类型分类的需求. 说明:(暂且不讨论这样分类 ...

  5. Python 类型的分类

    1.存储模型,对象可以保存多少个值.如果只能保存一个值,是原子类型.如果可以保存多个值,是容器类型.数值是原子类型,元组,列表,字典是容器类型.考虑字符串,按道理,字符串应该是容器类型,因为它包含多个 ...

  6. SQL string类型的数据按int类型排序 分类: SQL Server 2014-12-08 16:56 393人阅读 评论(0) 收藏

    说明: 我在做wms进销存软件时,发现一个问题:一张入库单(T_OutIn_BoxTop),入库扫描时要分成多箱,箱号(BoxTop_No)可以是数字也可以是字符串,所以箱号只能是字符串类型的,问题来 ...

  7. C#代码总结03---通过获取类型,分类对前台页面的控件进行赋值操作

    该方法: 一般用于将数据库中的基本信息字段显示到前台页面对应的字段控件中 private void InitViewZc(XxEntity model) { foreach (var info in ...

  8. API 接口设计中 Token 类型的分类与设计

    在实际的网站设计中我们经常会遇到用户数据的验证和加密的问题,如果实现单点,如果保证数据准确,如何放着重放,如何防止CSRF等等 其中,在所有的服务设计中,都不可避免的涉及到Token的设计. 目前,基 ...

  9. oracle 索引扫描类型的分类与构造

    1. INDEX RANGE SCAN--请记住这个INDEX RANGE SCAN扫描方式drop table t purge;create table t as select * from dba ...

随机推荐

  1. bootstrap_bootstrap中日历范围选择插件daterangepicker的使用

    1.引入脚本 <link rel="stylesheet" type="text/css" href="assets/css/bootstrap ...

  2. v8-su-root

    1.下载userdebug版本 2.设置模块打开develop options 3.勾选usb debugging 4.adb remount 5.解压SuperSU_N.7z(联系我索取)并push ...

  3. nginx 的多域名多https转发设置方法【转】

    version: 1.1(fixed) 修正一些错误基本环境:/etc/nginx/nginx.conf #保持/etc/nginx/ssl/    #ssl认证文件/etc/nginx/site-a ...

  4. j-linkV8固件更新(任意版本)

    在使用j-link v8调试程序时,容易出现固件丢失或出错的情况,导致电脑不能识别,j-link上面的灯不亮.我今天刚刚遇到了这个情况,于是就拆开外壳,在网上搜索资料,发现刷固件相关的还真多,但是有一 ...

  5. Django-模板语言和过滤器

    Django模板语言 首先模板只是一个文本文件,它可以生成任何基于文本的格式(HTML.XML.CSS等),模板中包含变量,在模板被渲染的时候替换为最终的值,以及控制模板逻辑的标签. 变量使用{{ 变 ...

  6. mysqlbinlog的日志类型

    一.mysqlbinlog简介 binlog又叫二进制日志文件,它会将mysql中所有修改数据库数据的Query以二进制的形式记录到日志文件中,如:create,insert,drop,update等 ...

  7. python网络编程-socket发送大数据包问题

    一:什么是socket大数据包发送问题 socket服务器端或者客户端在向对方发送的数据大于对方接受的缓存时,会出现第二次接受还接到上次命令发送的结果.这就出现象第一次接受结果不全,第二次接果出现第一 ...

  8. python基础-类的其他方法

    一.isinstance(obj,cls)检查是否obj是类的cls对象 # -*- coding:utf-8 -*- __author__ = 'shisanjun' class Foo(objec ...

  9. wpf image blur

    RenderOptions.BitmapScalingMode="NearestNeighbor"

  10. wpf tooltip 样式自定义

    <Style BasedOn="{StaticResource {x:Type ToolTip}}" TargetType="ToolTip"> & ...