博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
YYAsyncLayer 源码解析
阅读量:6504 次
发布时间:2019-06-24

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

YYAsyncLayer的示例

示例中

[[YYTransaction transactionWithTarget:self selector:@selector(contentsNeedUpdated)] commit];- (void)contentsNeedUpdated {    // do update    [self.layer setNeedsDisplay];}- (YYAsyncLayerDisplayTask *)newAsyncDisplayTask {    //..}复制代码

1 YYTransaction

看看 YYTransaction , 根据名字 这应该是 处理事物相关的 类。

不得不说 这个注释真好

/** YYTransaction let you perform a selector once before current runloop sleep. */@interface YYTransaction : NSObject复制代码

可以看出 YYTransaction 是 用来将 selector 在 runloop sleep 前 提交到 runloop 中 处理的。

YYTransaction 存储了 targetselector 用来在runloop observer callback 中执行对应方法

1.1 Commit

注意commit 中的注释,如果 相同的 transaction 已经提交到 runloop 中了,这个方法什么都不会做.

/** Commit the trancaction to main runloop. @discussion It will perform the selector on the target once before main runloop's current loop sleep. **If the same transaction (same target and same selector) has  already commit to runloop in this loop, this method do nothing.** */- (void)commit;复制代码

这是怎么实现的呢?

- (void)commit {    if (!_target || !_selector) return;    // 在Commit 中 做 单例的初始化 很好 隐藏了很多细节,使用着通过 简单的调用即可 添加 transcation    YYTransactionSetup();    [transactionSet addObject:self];}复制代码

可以注意到transactionSet 既然是Set 那么 是不会存在两个相同的元素,系统会自动删掉一个元素

Objective-C 中 通过 isEqual: 方法 来测试和其他对象的想等性

通过重写isEqual:hash 来支持根据 _selector,_target 判断想等性.

- (NSUInteger)hash {    long v1 = (long)((void *)_selector);    long v2 = (long)_target;    return v1 ^ v2;}- (BOOL)isEqual:(id)object {    if (self == object) return YES;    if (![object isMemberOfClass:self.class]) return NO;    YYTransaction *other = object;    return other.selector == _selector && other.target == _target;}复制代码

1.2 Observe RunLoop

YYTransaction 通过观察Runloop的waiting或Exit状态 ,通过回调,执行 transactionSet 中的 transaction

// 注册 Runloop Observerstatic void YYTransactionSetup() {    static dispatch_once_t onceToken;    dispatch_once(&onceToken, ^{        transactionSet = [NSMutableSet new];        CFRunLoopRef runloop = CFRunLoopGetMain();        CFRunLoopObserverRef observer;        observer = CFRunLoopObserverCreate(CFAllocatorGetDefault(),                                           kCFRunLoopBeforeWaiting | kCFRunLoopExit,                                           true,      // repeat                                           0xFFFFFF,  // after CATransaction(2000000)                                           YYRunLoopObserverCallBack, NULL);        CFRunLoopAddObserver(runloop, observer, kCFRunLoopCommonModes);        CFRelease(observer);    });}// Runloop Observer Callbackstatic void YYRunLoopObserverCallBack(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info) {    if (transactionSet.count == 0) return;    NSSet *currentSet = transactionSet;    // 更新 trasactionSet 保证 callback 执行后,对象不会被持有    transactionSet = [NSMutableSet new];    [currentSet enumerateObjectsUsingBlock:^(YYTransaction *transaction, BOOL *stop) {#pragma clang diagnostic push#pragma clang diagnostic ignored "-Warc-performSelector-leaks"        [transaction.target performSelector:transaction.selector];#pragma clang diagnostic pop    }];}复制代码

2 YYAsyncLayer

/** The YYAsyncLayer class is a subclass of CALayer used for render contents asynchronously. @discussion When the layer need update it's contents, it will ask the delegate  for a async display task to render the contents in a background queue. */@interface YYAsyncLayer : CALayer复制代码

可以看到 YYAsyncLayerDelegate 的 newAsyncDisplayTask 是提供了 YYAsyncLayer 需要在后台队列绘制的内容.

2.1 YYAsyncLayerDisplayTask

YYAsyncLayerDisplayTask 有如下的属性

@property (nullable, nonatomic, copy) void (^willDisplay)(CALayer *layer);- display@property (nullable, nonatomic, copy) void (^display)(CGContextRef context, CGSize size, BOOL(^isCancelled)(void));@property (nullable, nonatomic, copy) void (^didDisplay)(CALayer *layer, BOOL finished);复制代码

display 在mainthread或者background thread调用 这要求 display 应该是线程安全的

willdisplay 和 didDisplay 在 mainthread 调用。

newAsyncDisplayTask 是提供了 YYAsyncLayer 需要在后台队列绘制的内容.

2.1 YYAsyncLayer 异步绘制

通过 重写display 方法,异步绘制 self.contents

- (void)display {    super.contents = super.contents;    [self _displayAsync:_displaysAsynchronously];}复制代码

- (void)_displayAsync:(BOOL)async 中在后台队列执行 task.display 的 block 进行绘制任务,最后在主线程中 将绘制结果的图片赋值给 contents.

self.contents = (__bridge id)(image.CGImage);复制代码

2.2 取消绘制任务

当 TableView 快速滑动时,会有大量异步绘制任务提交到后台线程去执行。但是有时滑动速度过快时,绘制任务还没有完成就可能已经被取消了。如果这时仍然继续绘制,就会造成大量的 CPU 资源浪费,甚至阻塞线程并造成后续的绘制任务迟迟无法完成。

- (void)setNeedsDisplay {    [self _cancelAsyncDisplay];    [super setNeedsDisplay];}- (void)_cancelAsyncDisplay {    [_sentinel increase];}复制代码

这个跟 tableview 的 cell 重用有关

由于 cell 重用,那么 当重用的cell绘制新的内容时,就会调用setNeedDisplay 方法.
这是可以再次取消上一次的后台绘制任务,在进行新的绘制.

利用 YYSentinel 来完成任务的取消

/** YYSentinel is a thread safe incrementing counter.  It may be used in some multi-threaded situation. */@interface YYSentinel : NSObject复制代码

value 用来保存任务刚开始时 sentinel.value

int32_t value = sentinel.value;复制代码

如果任务执行过程中 发现snetinel.value 和 保存的value,则就是认为任务以及取消了

BOOL (^isCancelled)() = ^BOOL() {    return value != sentinel.value;};复制代码

3 如何使用

  1. YYAsyncLayerDelegate 的 - (YYAsyncLayerDisplayTask *)newAsyncDisplayTask 提供了绘制所需的task
  2. 在设置可以涉及到 视图内容改变的 操作时,[[YYTransaction transactionWithTarget:self selector:@selector(contentsNeedUpdated)] commit];
  3. contentsNeedUpdated 的操作就是 [self.layer setNeedsDisplay]; 更新视图

菜?一个,望大佬多指教

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

你可能感兴趣的文章
Mysql
查看>>
POJ-1860-Currency Exchange
查看>>
跨越企业的“中等收入陷阱”
查看>>
Android 开发者必知的开发资源
查看>>
软件工程技术基础-(软件复用技术)
查看>>
给django视图类添加装饰器
查看>>
简述 clearfix 的原理
查看>>
【Project Euler】530 GCD of Divisors 莫比乌斯反演
查看>>
luogu P1280 尼克的任务 序列DP
查看>>
iphone UIView的一些基本方法理解
查看>>
sys.check_constraints
查看>>
vue问题
查看>>
ThinkPHP 框架学习
查看>>
css3箭头效果
查看>>
MathType在手,公式不求人!
查看>>
测试用例设计
查看>>
三层架构
查看>>
Python变量类型(l整型,长整形,浮点型,复数,列表,元组,字典)学习
查看>>
解决方案(.sln)文件
查看>>
【Treap】bzoj1588-HNOI2002营业额统计
查看>>