今天就系统的讨论一下Masonry对FSP的影响,以及如何更好的使用Masonry。若是你对iOS开发足够熟悉的话,那么对Masonry框架应该不陌生。简单的说,Masonry的诞生让AutoLayout的使用更为优雅,让控件的布局更为方便。使用辩证的观点来看一个事物的话,凡事都有两面性,Masonry的使用也不例外。Masonry框架的使用不当会直接影响当UI的FPS。今天咱们就来讨论一下在使用Masonry时的一些误区,看一下那些影响性能的使用方式。本篇博客咱们依然会依托于Demo来叙述的一些东西html
1、Demo综述git
1.运行效果github
先入为主,本篇博客的内容依然是依托于咱们特地为本篇博客所打造的Demo的,首先咱们先来看一下Demo运行起来是怎样的效果,经过Demo咱们能够看到那些问题,以及这些问题是如何被解决的。下方就是咱们本篇博客所涉及Demo的运行效果。面试
从下方的运行效果不难看出,咱们是分了6种状况来观察和判断Masonry的各类使用方式对FPS的影响如何。上方经过六个SegmentControl能够去切换Cell的布局方式。固然每种布局方式所呈现出来的Cell是相同的。这样也是作实验时保持实验项改变其余项保持一致的原则。咱们能够经过右下方FPS指示器来直观的感觉一下FPS的变化趋势。下方这个FPS显示控件是从咱们以前的Demo中拿过来的。以前的Demo也是关于FPS优化的,只不过是关于Cell高度动态计算的FPS优化,详情请移步于《iOS开发之多种Cell高度自适应实现方案的UI流畅度分析》。数组
下方Cell中所显示的数据时随机生成的,左边的Image也是随机取的。右边的Title和Detail都是NSAttributedString而且下方的有些Detail有可能为空。若是某一条的Detail为空,那么该条Detail下方的全部内容的布局上移。稍后会详细的介绍该Demo以及其中的技术点。网络
二、模拟网络请求框架
上面Cell中显示的数据是经过模拟网络数据来获取的,下方就是咱们的模拟网络层的相关代码。毕竟是Demo,而且Demo的重点不在网络层上,下方就简单的写了一下,代码比较简单。就是一个单例+一个模拟数据随机生成的方法,而后把这个随机生成的数据经过Block回调到网络层的使用者上。具体代码以下所示:oop
三、上述Cell的基类XBaseTableViewCell布局
上面每种Segment中的Cell都是一种独立的Cell类型,不过这些Cell有一个共同的父类。而这个父类就负责来处理这些Cell所共用的逻辑。下方的这个XBaseTableViewCell就是上述显示的全部Cell的基类。其中声明并初始化了Cell上的全部控件。而且提供了相关的设置值的方法。性能
从下方的代码中不难看出,有两个方法是须要子类进行重写的,一个是给控件添加布局的方法addLayoutSubviews, 另外一个就是更新布局的方法updateLayoutSubviews方法。每一个子类中都会对这两个方法进行重写并给出不一样的布局方式。
四、Segment中切换Cell的代码
下方是在相应的VC中的点击SegmentControl的逻辑代码, 点击不一样的Segment会选择不一样的Cell而后刷新TableView,代码比较简单就不作过多的赘述了。
2、对上述各类的布局方式进行分析
接下来要作的事情就是分析一下每种布局方式对FSP的影响,下方会对不一样的布局状况使用Instrument进行分析并看一下具体的数据。下方分别讨论了只使用Masonry的Update操做、Remake操做、先Make后Update、Frame操做以及先Make后Frame操做。
一、update
首先咱们来看一下update操做。也就是使用update直接给控件赋值,这是比较偷懒的一种操做。由于在咱们的Demo中在设置cell的值时会更新一些控件的UI布局,全部咱们索性就直接使用Masonry的update,直接给控件添加约束。在Masonry中的update操做有个特色,就是update一个约束会先在已添加的约束数组中找到该约束,而后更新该约束,若是找不到就install添加相应的约束。从这个update的功能来看其效率是比较低的。
我能够先看一下代码实现,在子类XUpdateLayoutTableViewCell中,重写了addLayoutSubviews和updateLayoutSubviews两个方法。在updateLayoutSubviews方法中,为全部的控件使用update的方式添加约束。下方这样写会在每次设置值的时候都会调用下方的updateLayoutSubviews方法,这样就会更新cell上的控件的全部布局,固然,不建议这样去作,由于这样会更新那些不须要更新的约束。之因此今天罗列出来,是由于在开发中下方的问题确实存在,也许是由于时间紧张,也许是由于其余缘由致使的下方这种代码实现。
咱们先来使用Instruments跑一下上述的Demo,而后直观的感觉一下该Demo的Core Animation的直观表现。下方就是咱们将SegmentControl切换到Update时所对应的FPS数据。从下方的数据咱们不难看出,直接用Update添加更新约束这种作法是比较影响FPS的。固然,Cell中还会使用到属性字符串,这个咱们稍后会讨论一下的。
咱们能够来跑一下Update状态下的Time Profile。以下所示,从下方的结果中不难看出,在Cell更新数据时,有两块的操做比较耗时。一个是Masonry的update操做,另外一个则是Label设置NSAttributedString的操做。由于咱们使用的每一个Label都会赋值一个属性字符串,这个是比较耗时的操做。还有一个要明确一点的是,属性字符串的建立和生成并不会占用多少时间,而属性字符串的赋值和渲染所占用的时间是比较多的,这一点从下方的Time Profile中也是不难看出的。
二、remake
接下来咱们在来看一下Remake操做,从下方的Core Animation的结果中不难看出,其所表现出来的效果还不如使用Update操做呢。下方的FPS比Update要低一些,这也与remake自身的操做有关系,remake从字面意思来看就是从新制做,若是以前已经添加过约束的话就先移除掉,而后再添加新的约束。
下方是Remake所对应的Time Profile,从结果中咱们能够看出布局更新占用了66.6%的耗时,并且33%的install耗时中uninstall占用了10%左右的开销。在Masonry中remake效率是最低的。稍后咱们会继续进行讨论。
三、make + update
讨论完update和remake, 咱们来讨论一下使用Masonry的常规作法。就是使用make来初始化控件的布局,使用update来更新所须要更新的约束。由于代码比较简单,就不一一往上贴了,可是跑一下使用Instrument跑一下仍是颇有必要的。下方是make + update 的方式的Core Animation所跑出来的结果。但从下方的FSP结果来看,仍是要比以前只使用update或者remake的效果要好一些的,不过下方的FPS仍是不高,稍后咱们会将下方的数据进行细化。
该部分的Time Profile就不跑了,由于设置值的时候咱们依然采用的Update来更新的约束,只不过不是更新全部的约束,而是更新那些只须要更新的约束。由于更新的约束的量会少一些,全部FPS的表现效果会比以前更新全部的约束会更好一些。make + update的方式会是FPS稍微改善一些,可是从下方的图中咱们能够看出,改善的并非特别好。
四、frame + frame
接下来,咱们就不用Masonry来布局了,咱们直接使用Frame布局。由于Autolayout最终仍然会转换为Frame布局的,很显然Frame布局在性能方面是优于Autolayout布局的。接下来咱们就来使用Frame布局而后使用Frame更新。下方的FPS还算说得过去,不过还不是满格,其大部分缘由就是由于NSAttrubitedString的缘由了。
咱们能够看一下更新Frame的Time Profile,以下所示。从下方的截图中,咱们不难看出update frame的时间占比只占到了2.5%,以前更新约束能占到**60%**左右,能够看到使用Frame布局的好处。从下方的分析结果中不难看出,如今影响FPS主要因素已经从更新布局转化到了AttributeString的设置。这也是上述FPS没有满格的缘由。
五、make + frame
Masonry的诞生就是为了方便控件布局的,Frame布局不够灵活,适配起来比较繁琐,因此才有了AutoLayout。不过虽然AutoLayout能够很方便的适配屏幕,但是其性能方面表现的不是特别好。那么咱们可不能够将二者进行结合呢。也就是说使用make来初始化控件的布局,使用Frame来更新布局。固然这一过程不是简单的在设置值的时候更新一下Frame就能够的,由于在Cell设置值的时候去更新Frame是没用的,由于更新完Frame后,在渲染显示的时候,仍是会按照AutoLayout的布局来显示的。咱们须要作的是将Frame布局放到Autolayout布局以后,此处咱们要作的就是将更新Frame的相关代码放到下一个Runloop中来执行。更新Frame代码以下:
在cell中是make初始化控件布局,使用Frame更新布局,和Frame+Frame的方式差很少,只不过是使用Masonry布局时,在首屏加载的时候不如Frame布局,之后更新是同样的。下方是使用Masonry+Frame的形式的Core Animation的结果。效果虽然比上一部分会稍微差一些,可是最终的效果仍是满Ok的。
** 3、总结**
本篇博客只讨论Masonry的布局方式对FPS的影响,至于上述的NSAttributeString的问题不作过多赘述了。若是根据业务须要,有许多富文本的展现影响了FPS的话,那么能够采用其余的方式来进行优化,好比使用AsynDisplayKit所提供的相关Node进行显示等等。在博客的结尾,仍是有必要作一个总结的。
下方是咱们在代码中更为细化的数据,从数据中不难看出Remake的性能是最差的,因此咱们在使用Masonry时尽可能要少使用Remake。对控件的更新只一味的选择使用Update也不是一个好的选择,若是要使用Masonry框架还要对控件进行布局更新的话,最好是把那些不变的约束和须要更新的约束分开。使用make来添加相关约束,使用update来修改须要更新的约束。固然使用Frame布局的性能会好一些,不过布局过程过于繁琐不便于进行屏幕的适配。固然也可使用Masonry进行布局使用Frame进行布局的更新,固然须要注意的是Frame布局更新的时机,需在Autolayout加载的时机后进行。
下方是进行了统一的数据统计,固然是针对本篇博客所对应的Demo的。下方表格中统计了一次更新cell布局所采用的不一样方式的平均时间,从下方的数据中咱们不难看粗Remake的更新布局用时最多,消耗了12+ms, 而Update全部的约束用时也是很多,一次更新布局使用了9+ms。而只更新须要更新的布局用时7+ms, 稍微要比更新全部的布局要好一些。固然直接修改Frame的用时最少,只用了0.06+ms的时间,从该数据能够直观的感觉到Frame布局的效率性。
而右边还给出了一个属性字符串的建立和赋值的用时,其中咱们能够看到,属性字符串的建立耗时并非太多,而比较耗时的是属性字符串的赋值,每次赋值占用了0.7ms, 若是是10个的话,那么赋值时间就是7ms, 若是属性字符串的内容再复杂一些,那么用时确定会比这个高。固然咱们可使用第三方提供的一些控件和方法将这部分时间给优化掉,这个能够放到之后再讨论。
今天的博客就到这儿吧,目的是在使用Masonry时要合理的进行使用,有必要时,可使用Frame进行布局。
上述demo -github分享连接:github.com/lizelu/FPSP…
你认为如何?请经过加咱们的交流群 点击此处进交流群 ,来一块儿交流或者发布您的问题,意见或反馈。
做者:青玉伏案 出处:www.cnblogs.com/ludashi/