iOS开源库源码解析之AsyncDispalyKit

一些有深度的博客我会同步到自己的Github上

这个源码解析系列的文章


前言

最近心血来潮,想研究下FaceBook的AsnycDispalyKit的源代码,学习一些界面优化的技术以及编码风格。这篇文章,会详细的记录下我认为对新手有用的部分。后面有空的时候,继续研究其他几个iOS开发很流行的库-AFNetworking,SDWebImage,MBProgressHud,Mantle等`。AsnycDisplayKit是一个非常庞大的库,所以我尽量捞干的讲。

关于AsyncDisplayKit

如果只是想优化界面,那么可以用AsyncDisplayKit来重写哪些性能要求比较高的部分

对了,阅读YYKit的作者ibireme的《iOS 保持界面流畅的技巧》一文对我的启发很大,建议读者可以看看他的文章,真的写得很好,是国内少有的iOS开发大神。


界面顿卡的原因

iOS的屏幕是60fps,也就是说,每一帧的间隔是1/60s,大概16.7ms
每一帧显示需要三步

  • CPU计算好视图(UIView)的大小,位置,对图片进行解码,绘制好纹理交给GPU
  • GPU根据纹理,顶点进行空间变换,渲染后放到帧缓冲区
  • 每当帧信号到达的时候,从帧缓冲区取一帧,显示到屏幕上

也就是说,整个CPU+GPU处理的时间是16.7ms,如果超过这个时间,那么当前绘制的一帧就没办法放到帧缓冲区,帧信号到达的时候,取的还是上一帧的数据。也就是造成了界面没有变化,显示顿卡。也就是说,为了解决顿卡,一般要从CPU和GPU两个角度来考虑

CPU限制

  • 对象的创建,释放,属性调整。这里尤其要提一下属性调整,CALayer的属性调整的时候是会创建隐式动画的,是比较损耗性能的。
  • 视图和文本的布局计算,AutoLayout的布局计算都是在主线程上的,所以占用CPU时间也很多 。
  • 文本渲染,诸如UILabel和UITextview都是在主线程渲染的
  • 图片的解码,这里要提到的是,UIImage只有在交给GPU之前的一瞬间,CPU才会对其解码。

GPU限制

  • 视图的混合,比如一个界面十几层的视图叠加到一起,GPU不得不计算每个像素点药显示的像素
  • 视图的Mask,比如圆角什么的,会触发离屏渲染,占用GPU时间。
  • 半透明,GPU不得不进行数学计算,如果是不透明的,CPU只需要取上层的就可以了
  • 浮点数像素

AsnycDisplayKit通过很多技巧来解决这些问题,后文我会一点点分析如何实现的。


AsyncDisplayKit是啥

这是Facebook推出的一个框架,用在Paper的App中。用来保证复杂的界面交互的时候,也不会掉帧。
通过名字就可以看出来,AsyncDisplay就是异步加载控件。了解UIKit的同学都知道,UIKit的中的UIView和CALayer的布局和渲染都是在主线程上进行的,当界面复杂的时候,也就会占用大量时间导致掉帧。这个框架是建立在UIKit之上的,对UIView进行了进一步的封装-Node。Node支持异步的绘制UIView。Asnyc有一个原则

  • 能放到后台执行的代码就尽量放到后台,不能放到后台执行的代码就尽量优化(比如用Runloop对任务进行拆分)

ASDealloc2MainObject

这个类中,AsnycDisplayKit重新定义了Release和Reatin方法来让一个类支持自己引用计数,可以强制的让对象在主线程dealloc,不过这个文件是MRC的,也就是要在build setting中添加-fno-objc-arc

那么,为什么要强制的在主线程dealloc呢?因为UIKit的对象不是线程安全的,只能在主线程上进行dealloc

Tips:

1.可以在文件中,添加如下代码

#if __has_feature(objc_arc)
#error This file must be compiled without ARC. Use -fno-objc-arc.
#endif

来让编译器检查本文件只能在MRC条件下编译。
关于如何重写Release和Reatian,可以在这个文件里找到_AS-objc-internal.h

2.由于Define只是在编译期进行简单替换,可以通过#defeine的方式为类条件添加代码


大量的断言和宏定义

通过阅读源代码可以发现,代码中使用了大量的宏和断言

- (void)dealloc
{
  ASDisplayNodeAssertMainThread();
  //Other codes
}

其中ASDisplayNodeAssertMainThread()为宏定义,

  #define ASDisplayNodeAssertWithSignal(condition, description, ...) NSAssert(condition, description, ##__VA_ARGS__)
 #define ASDisplayNodeAssertMainThread() ASDisplayNodeAssertWithSignal([NSThread isMainThread], nil, @"This method must be called on the main thread")

对于断言和宏定义使用很少的同学,可以看看这个头文件,会对你很有帮助。

Tips:
合理的使用断言NSAssert,能够让你的代码在更早的地方出现问题,方便发现问题进行调试。在XCode 7中,NSAssert默认只会在Debug模式下起作用,在release模式下不会起作用,


Objective C++

在AsyncDisplay中,可以看到很多.mm后缀的文件,例如

Objective C++和Objective C类似,它的文件组成由一个.h和一个.mm组成&#x

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值