YYAsyncLayer

1.YYSentinel

哨兵类,有一个私有变量 value 用来存储计数器值(线程安全),还有一个自增的方法.

2. YYTransaction

内部使用监听 Runloop 的方式,将事务分发到 RunLoop 空闲的时候来做,同时内部复写了hash/isEqual,然后使用静态 NSSet 来保存 transaction,绝妙之处就在于对于同一个 target 的 selector 在同一次 RunLoop 提交多次时也只执行一次。此处正是使用 hash/isEqual 来保证 Set 重复添加同一事务是不被响应的。同时在 RunLoop 分发完成之后又会将静态 Set 重置,来保证每次 RunLoop 空闲都会执行且不会重复执行。

3. YYAsyncLayer (继承自 CALayer)

dispatch_once 创建串行的优先级为 QOS_CLASS_USER_INITIATED (>= iOS8),iOS7 优先级为 default 的队列。dispatch_queue_create 函数,在iOS8之前,不能在创建时设定优先级,所以用 dispatch_set_target_queue 设置优先级,详见代码。
 静态函数用来创建两种队列,一种是异步渲染队列(较高优先级),一种是释放队列(优先级较低).

static dispatch_queue_t YYAsyncLayerGetDisplayQueue() {
#define MAX_QUEUE_COUNT 16
static int queueCount;
static dispatch_queue_t queues[MAX_QUEUE_COUNT];
static dispatch_once_t onceToken;
static int32_t counter = 0;
dispatch_once(&onceToken, ^{
queueCount = (int)[NSProcessInfo processInfo].activeProcessorCount;
queueCount = queueCount < 1 ? 1 : queueCount > MAX_QUEUE_COUNT ? MAX_QUEUE_COUNT : queueCount;
if ([UIDevice currentDevice].systemVersion.floatValue >= 8.0) {
for (NSUInteger i = 0; i < queueCount; i++) {
dispatch_queue_attr_t attr = dispatch_queue_attr_make_with_qos_class(DISPATCH_QUEUE_SERIAL, QOS_CLASS_USER_INITIATED, 0);
queues[i] = dispatch_queue_create("com.ibireme.yykit.render", attr);
}
} else {
for (NSUInteger i = 0; i < queueCount; i++) {
queues[i] = dispatch_queue_create("com.ibireme.yykit.render", DISPATCH_QUEUE_SERIAL);
dispatch_set_target_queue(queues[i], dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0));
}
}
});
int32_t cur = OSAtomicIncrement32(&counter);
if (cur < 0) cur = -cur;
return queues[(cur) % queueCount];
#undef MAX_QUEUE_COUNT
#endif
}

YYAsyncLayer 的 delegate 必须实现协议 <YYAsyncLayerDelegate> 中的方法,主要是用来返回一个YYAsyncLayerDisplayTask,该 task 可以指定一系列渲染相关的行为 willdisplay/display/diddisplay.

大量使用 OSAtomicIncrement32,由于该函数对某个值进行自增计算,而且是线程安全的,所以被用来作为绘制时的标记位(哨兵).异步绘制的关键在于每次调用 setNeedDisplay 时都会将哨兵变量自增,在 display 方法中,根据这个哨兵变量来确定是否继续绘制还是停止绘制.

YYSentinel *sentinel = _sentinel;
int32_t value = sentinel.value;
// 这个 block 会在异步绘制的队列(上述方法创建的)中调用,来进行判断是否取消.
BOOL (^isCancelled)() = ^BOOL() {
return value != sentinel.value;
};
Show your support

Clapping shows how much you appreciated Vong’s story.