AsyncDisplayKit是Facebook开源的一套用于iOS界面流畅的异步绘制UI的框架
Runloop
https://juejin.im/post/5b59770c5188251b166f0638
通过监听UI主线程runloop的BeforeWaiting和Exit事件,将子线程中的修改合并到UI主线程

封装的group中通过int型数据表示group的enter、leave和开启的线程数
void ASAsyncTransactionQueue::GroupImpl::enter()
{
std::lock_guard<std::mutex> l(_queue._mutex);//锁
++_pendingOperations;
NSLog(@"add _pendingOperations = %@",[NSThread currentThread]);
}
void ASAsyncTransactionQueue::GroupImpl::leave()
{
std::lock_guard<std::mutex> l(_queue._mutex);//锁
--_pendingOperations;
...
}
++entry._threadCount;//开启的线程数
dispatch_async(queue, ^{
std::unique_lock<std::mutex> lock(q._mutex);
// go until there are no more pending operations
while (!entry._operationQueue.empty()) {
Operation operation = entry.popNextOperation(respectPriority);
lock.unlock();
if (operation._block) {
ASProfilingSignpostStart(3, operation._block);
operation._block();
ASProfilingSignpostEnd(3, operation._block);
}
operation._group->leave();
operation._block = nil; // the block must be freed while mutex is unlocked
lock.lock();
}
--entry._threadCount;
if (entry._threadCount == 0) {
NSCAssert(entry._operationQueue.empty() || entry._operationPriorityMap.empty(), @"No working threads but operations are still scheduled"); // this shouldn't happen
q._entries.erase(queue);
}
});
runloop observer优先级在CA动画之后

static void _transactionGroupRunLoopObserverCallback(CFRunLoopObserverRef observer, CFRunLoopActivity activity, void *info)
{
ASDisplayNodeCAssertMainThread();
_ASAsyncTransactionGroup *group = (__bridge _ASAsyncTransactionGroup *)info;
[group commit];//将需要主线程处理的操作提交到主线程
}
并发操作及并发数控制
void ASAsyncTransactionQueue::GroupImpl::schedule(NSInteger priority, dispatch_queue_t queue, dispatch_block_t block)
{
ASAsyncTransactionQueue &q = _queue;
std::lock_guard<std::mutex> l(q._mutex);
DispatchEntry &entry = q._entries[queue];
Operation operation;
operation._block = block;
operation._group = this;
operation._priority = priority;
entry.pushOperation(operation);
++_pendingOperations; // enter group
#if ASDISPLAYNODE_DELAY_DISPLAY
NSUInteger maxThreads = 1;
#else
//当前激活的CPU总数
NSUInteger maxThreads = [NSProcessInfo processInfo].activeProcessorCount * 2;
// Bit questionable maybe - we can give main thread more CPU time during tracking;
if ([[NSRunLoop mainRunLoop].currentMode isEqualToString:UITrackingRunLoopMode])
--maxThreads;
#endif
//控制并发数量
if (entry._threadCount < maxThreads) { // we need to spawn another thread
// first thread will take operations in queue order (regardless of priority), other threads will respect priority
bool respectPriority = entry._threadCount > 0;
++entry._threadCount;
dispatch_async(queue, ^{
std::unique_lock<std::mutex> lock(q._mutex);
// go until there are no more pending operations
while (!entry._operationQueue.empty()) {
Operation operation = entry.popNextOperation(respectPriority);
lock.unlock();
if (operation._block) {
ASProfilingSignpostStart(3, operation._block);
operation._block();
ASProfilingSignpostEnd(3, operation._block);
}
operation._group->leave();
operation._block = nil; // the block must be freed while mutex is unlocked
lock.lock();
}
--entry._threadCount;
if (entry._threadCount == 0) {
NSCAssert(entry._operationQueue.empty() || entry._operationPriorityMap.empty(), @"No working threads but operations are still scheduled"); // this shouldn't happen
q._entries.erase(queue);
}
});
}
}
如果操作在实际执行之前被取消,则不再执行
_group->schedule(priority, queue, ^{
@autoreleasepool {
if (self.state != ASAsyncTransactionStateCanceled) {
operation.value = block();
}
}
});
绘制
if (shouldBeginRasterizing) {
// Collect displayBlocks for all descendants.
NSMutableArray *displayBlocks = [NSMutableArray array];
[self _recursivelyRasterizeSelfAndSublayersWithIsCancelledBlock:isCancelledBlock displayBlocks:displayBlocks];
CHECK_CANCELLED_AND_RETURN_NIL();
// If [UIColor clearColor] or another semitransparent background color is used, include alpha channel when rasterizing.
// Unlike CALayer drawing, we include the backgroundColor as a base during rasterization.
opaque = opaque && CGColorGetAlpha(self.backgroundColor.CGColor) == 1.0f;
displayBlock = ^id{
CHECK_CANCELLED_AND_RETURN_NIL();
UIGraphicsBeginImageContextWithOptions(bounds.size, opaque, contentsScaleForDisplay);
for (dispatch_block_t block in displayBlocks) {
CHECK_CANCELLED_AND_RETURN_NIL(UIGraphicsEndImageContext());
block();
}
UIImage *image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
ASDN_DELAY_FOR_DISPLAY();
return image;
};
} else {
displayBlock = ^id{
CHECK_CANCELLED_AND_RETURN_NIL();
if (shouldCreateGraphicsContext) {
UIGraphicsBeginImageContextWithOptions(bounds.size, opaque, contentsScaleForDisplay);
CHECK_CANCELLED_AND_RETURN_NIL( UIGraphicsEndImageContext(); );
}
CGContextRef currentContext = UIGraphicsGetCurrentContext();
UIImage *image = nil;
ASDisplayNodeContextModifier willDisplayNodeContentWithRenderingContext = nil;
ASDisplayNodeContextModifier didDisplayNodeContentWithRenderingContext = nil;
if (currentContext) {
__instanceLock__.lock();
willDisplayNodeContentWithRenderingContext = _willDisplayNodeContentWithRenderingContext;
didDisplayNodeContentWithRenderingContext = _didDisplayNodeContentWithRenderingContext;
__instanceLock__.unlock();
}
// For -display methods, we don't have a context, and thus will not call the _willDisplayNodeContentWithRenderingContext or
// _didDisplayNodeContentWithRenderingContext blocks. It's up to the implementation of -display... to do what it needs.
if (willDisplayNodeContentWithRenderingContext != nil) {
willDisplayNodeContentWithRenderingContext(currentContext);
}
// Decide if we use a class or instance method to draw or display.
id object = usesInstanceMethodDisplay ? self : [self class];
if (usesImageDisplay) { // If we are using a display method, we'll get an image back directly.
image = [object displayWithParameters:drawParameters
isCancelled:isCancelledBlock];
} else if (usesDrawRect) { // If we're using a draw method, this will operate on the currentContext.
[object drawRect:bounds withParameters:drawParameters
isCancelled:isCancelledBlock isRasterizing:rasterizing];
}
if (didDisplayNodeContentWithRenderingContext != nil) {
didDisplayNodeContentWithRenderingContext(currentContext);
}
if (shouldCreateGraphicsContext) {
CHECK_CANCELLED_AND_RETURN_NIL( UIGraphicsEndImageContext(); );
image = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
}
ASDN_DELAY_FOR_DISPLAY();
return image;
};
}
图片解码
当你用 UIImage 或 CGImageSource 的那几个方法创建图片时,图片数据并不会立刻解码。图片设置到 UIImageView 或者 CALayer.contents 中去,并且 CALayer 被提交到 GPU 前,CGImage 中的数据才会得到解码。这一步是发生在主线程的,并且不可避免。如果想要绕开这个机制,常见的做法是在后台线程先把图片绘制到 CGBitmapContext 中,然后从 Bitmap 直接创建图片。
CoreGraphic 方法通常都是线程安全的
文本渲染
- 特殊字体富文本才用到CoreText
- 普通字体用TextKit异步渲染 https://www.jianshu.com/p/70ed6ec0cf28
网友评论