APP性能优化汇总

简介

随着IPhone手机市场的快速发展,苹果对 IOS APP的审核和要求也更加的苛刻。所以APP的性能是决定能否上架,能否给用户更好的用户体验的关键因素之一。性能优化主要包括启动速度、UI反馈与响应、列表的滚动流畅性、内存是否泄漏、图形动画、等方面。

造成卡顿的原因

VSync信号到来后,系统图形服务会通过CADisplayLink等机制通知AppApp主线程开始在CPU中计算显示内容,比如视图的创建、布局计算、图片解码、文本绘制等。随后CPU会将计算好的内容提交到GPU去,由GPU进行变换、合成、渲染。随后GPU会把渲染结果提交到帧缓冲区去,等待下一次VSync信号到来时显示到屏幕上。由于垂直同步的机制,如果在一个VSync时间内,CPU或者GPU没有完成内容提交,则那一帧就会被丢弃,等待下一次机会再显示,而这时显示屏会保留之前的内容不变。这就是界面卡顿的原因。

iOS 设备中的 CPU & GPU

  • CPU加载资源,对象创建,对象调整,对象销毁,布局计算,Autolayout,文本计算,文本渲染,图片的解码, 图像的绘制(Core Graphics)都是在CPU上面进行的。

  • GPU是一个专门为图形高并发计算而量身定做的处理单元,比CPU使用更少的电来完成工作并且GPU的浮点计算能力要超出CPU很多。GPU的渲染性能要比CPU高效很多,同时对系统的负载和消耗也更低一些,所以在开发中,我们应该尽量让CPU负责主线程的UI调动,把图形显示相关的工作交给GPU来处理,当涉及到光栅化等一些工作时,CPU也会参与进来,这点在后面再详细描述。

  • 相对于CPU来说,GPU能干的事情比较单一:接收提交的纹理(Texture)和顶点描述(三角形),应用变换(transform)、混合(合成)并渲染,然后输出到屏幕上。通常你所能看到的内容,主要也就是纹理(图片)和形状(三角模拟的矢量图形)两类。

CPU & GPU 协同工作


由上图可知,要在屏幕上显示视图,需要CPUGPU一起协作,CPU计算好显示的内容提交到GPUGPU渲染完成后将结果放到帧缓存区,随后视频控制器会按照VSync信号逐行读取帧缓冲区的数据,经过可能的数模转换传递给显示器显示。

iOS使用的是双缓冲机制。即GPU会预先渲染好一帧放入一个缓冲区内(前帧缓存),让视频控制器读取,当下一帧渲染好后,GPU会直接把视频控制器的指针指向第二个缓冲器(后帧缓存)。当你视频控制器已经读完一帧,准备读下一帧的时候,GPU会等待显示器的VSync信号发出后,前帧缓存和后帧缓存会瞬间切换,后帧缓存会变成新的前帧缓存,同时旧的前帧缓存会变成新的后帧缓存。

TabViewCell性能分析

  • 重用思想。
    (1)我们不仅要对tableviewcollectionviewcell进行重用,而且还要对两者的HeardViewFooterView进行重用。
    (2)在开发中我们会用到很多重复的视图、模型,比如相似度很高的控件,数据基本类似的model,这时候我们应该考虑如何避免重复创建这些控件和模型,而是利用现有的最少的资源实现想要东西。
    (3)对于与重用标识符我们最好使用关键字Static去修饰重用标识符,确保重用标识符在内存中之内创建一次。
    (4)Views越多就意味着需要渲染的越多,所以消耗性能就越大,所以重用很重要。

  • Cell内容布局和Cell动态行高。对于相对复杂度、计算量比较大的布局,我们可以在Cell内容展示之前通过异步方式将展示内容的Frame、cell的行高,依赖关系提前计算好,并做动态缓存,在展示的时候直接将Frame赋值响应的控件。流程:如果缓存有Frame,直接给控件的frame赋值;如果缓存中没有,在Cell内容展示之前通过异步方式将展示内容的Frame和依赖关系提前计算好,给控件的frame赋值,并缓存好计算的frame

  • 通过instuments中的Core Animation调试。
    (1)图层混合。
    检测界面多个UI控件叠加的情况,对比图层颜色显示,如果显示为红色,说明使用透明或者半透明的控件,致使GPU计算这些这些图层最终的显示的颜色的时候消耗了大量的GPU资源。检测如果显示为绿色说明没有使用透明或者半透明的控件,性能好。
    优化:
    1,设置opaque 属性为true
    2,给View设置一个不透明的颜色,没有特殊需要设置白色即可。
    (2) 图片的颜色格式。
    检测Cell上的内容设置图片的颜色格式,如果GPU不支持当前图片的颜色格式,那么就会将图片交给GPU预先进行格式转化,转化成合适的格式需要消耗大量GPU资源,CoreAnimation会将这张图片标记为蓝色。那么GPU支持什么格式呢?苹果的GPU只解析32bit的颜色格式,如果使用Color Copied Images去调试发现是蓝色,那么就找设计去要张格式合适的图吧···
    (3)图片大小。
    查看图片大小是否正确显示。*如果image sizeimageView size不匹配,image会出现黄色。要尽可能的减少黄色的出现,因为image size与imageView size不匹配,会消耗资源压缩图片。
    (4)离屏渲染。
    离屏渲染(Off-Screen Rendering)指的是GPU在当前屏幕缓冲区以外新开辟一个缓冲区进行渲染操作。
    当前屏幕渲染(On-Screen Rendering ),指的是GPU的渲染操作是在当前用于显示的屏幕缓冲区中进行。
    离屏渲染的危害,离屏渲染会先在屏幕外创建新缓冲区,离屏渲染结束后,再从离屏切到当前屏幕, 把离屏的渲染结果显示到当前屏幕上,这个上下文切换的过程是非常消耗性能的,实际开发中尽可能避免离屏渲染。
    导致离屏渲染的原因:

layer.shadow
layer.allowsGroupOpacity or layer.allowsEdgeAntialiasing
layer.shouldRasterize
layer.mask
layer.masksToBounds && layer.cornerRadius

所以在Cell中尽量避免给控件图层使用shadow(阴影)、shouldRasterize(光栅化)、layer.masksToBounds && layer.cornerRadius(设置圆角)等属性。

  • 图片加载方式。
    (1)异步加载思想。
    加载图片,从内存中查找,如果内存中有数据,则显示图片。如果内存中没有图片,查找本地资源加载到内存,显示图片。如果本地没有数据,异步下载图片,图片下载完成后显示图片,并将图片资源加载到内存和本地。运用这种流程概念能大大优化图片加载,比如SDWebImage框架。
    (2)利用OC运行时机制实现Cell的滑动和加载图片。
    当tableview的cell上有需要从网络获取的图片的时候,滚动tableView,异步线程会去加载图片,加载完成后主线程就会设置cell的图片,但是会造成卡顿。可以让设置图片的任务在CFRunLoopDefaultMode下进行,当滚动tableView的时候,RunLoop是在 UITrackingRunLoopMode 下进行,不去设置图片,而是当停止的时候,再去设置图片。
-(void)viewDidLoad {
[super viewDidLoad];
// 只在NSDefaultRunLoopMode下执行(刷新图片)
[self.myImageView performSelector:@selector(setImage:) withObject:[UIImage imageNamed:@""] afterDelay:ti inModes:@[NSDefaultRunLoopMode]];    
}

(3)本地图片加载:从Bunder中加载图片有两种方式,imageWithNameimageWithContentOfFileimageWithName加载图片的时候会缓存图片到内存,不适合加载太大的图片,不然对消耗大量的内存性能。imageWithContentOfFile仅加载图片不会缓存,适合加载大图片且不会被反复使用的图片,节省内存,提高性能。
(4)图片颜色格式、图片大小:如CoreAnimation上介绍

  • Cell刷新。
    (1)尽量减少使用reloadData刷新Cell,尽可能使用局部刷新。
    (2)上下拉刷新功能,尽量设置一次刷新接受的数据条数在10条——15条之内,如果接受的数据过多,数据的处理的过程就越长,展示到UI上就非常慢,界面会出现短暂的空白或者很长时间才展示,体验很差。

  • DrawRect方法绘制自定义控件
    单元格中的内容可以在自定义Cell中使用DrawRect方法内自己绘制。

  • Cell高度固定,如果单元格的高度固定,直接使用RowHeight设置高度,性能比在Cell方法中设置高度要好。

避免使用大量的XIB文件。

因为一个程序中XIB文件都是动态加载的,在编译的时候就会被全部加载到内存中。如果程序中使用了大量的XIB文件,不仅影响启动程序的速度,而且还占用了大量内存空间,用户的体验大大折扣。

做任何事都不要阻塞主线程

苹果将所有关于UI操作的内容都放在主线程(UI线程)中,原因是将所有的UI资源放在一个线程中,而且在主线程中任何属性的生命都是用非原子类型的。
(1)为了避免资源抢夺的危险;
(2)为了不用为主线程中的资源枷锁,因为加锁是非常浪费系统资源的。
(3)主线的优先级最好,放在主线程中的操作执行的更快,响应的更加灵敏,用户体验非常好。
(4)所以任何关于UI的操作都要放在主线中去操作。而无关UI的操作最好放到子线程中操作,比如上传、下载、网络请求、定时器等耗时操作。

尽量在ViewWillAppear方法中少写代码。

因为ViewWillAppear实在视图出现之前执行,如果在这个方法中执行了很多操作,会导致程序启动很慢,影响用户体验。

集合的选择和使用

NSSArray:是有序的一组值。用index查询快,但是插入和删除慢。
NSDictionary:是无序的,通过key存储的,所以通过Key查找很快。
Sets:无序的一组值,通过Value查找快,删除和插入块。
所以合理的选择数据封装格式,也更够提高软件性能。

延迟加载(懒加载)

懒加载的思想也是很重要,苹果官方封装的UI控件,大部分都利用到了懒加载的思想,比如ViewControllerViewcell上的子视图等。懒加载是在用到的时候才回去加载,这样做不仅避免所用的控件同时添加到内存中,而且还防止了控件的重复创建。

声明对象时尽量避免使用New关键字,多使用init alloc.

因为使用init alloc系统会默认调用initWithZone方法分配内存空间,分配的原则是类型相近就近分配,而New只是简单初始化,内存分配随机,所以使用init alloc声明对象减少了系统寻找堆中对象浪费的资源。

缓存思想。

要想提高软件性能,开发过程中做好缓存至关重要。缓存那些经常使用到而用不怎么变化的数据。比如cell中的动态变化的行高,和图片,我们要为他们做好缓存,下次加载这些资源的时候我们就不要再重复请求,而浪费资源又消耗性能。

对开销大的对象进行约束。

比如我们经常使用的NSdateFormatter对象,初始化非常慢,Time Profile检测显示估算创建NSdateFormatter对象平均耗33ms左右,设置NSdateFormatter对象的属性也会花费大量时间。所以我们针对这样的对象要加以限制,
处理方式:
(1)用static关键字修饰NSdateFormatter对象。
(2)用GCD中的dispatch_once修饰NSdateFormatter对象。
(3)用懒加载的方式加载NSdateFormatter对象。这三种方式都是为了防止NSdateFormatter对象重复初始化,减小系统开销。

正确的选择加载数据的格式。

Josn格式的数据要比XML要快。不过josn更是适合小文档数据,因为josn数据和XML的dom格式数据都是一次性将整个数据文档加载到内存中,所以用于加载小数据更合适。加载大数据我们做好采用XML的SAX格式,因为SAX是一点点的加载到内存,然后一点点的解析,是流式的,所以更适合大数据的加载。

数据存储格式的选择。

  • Plist格式和对象序列化都需要读写操作,不适合存储大数据,更适合小数据的存储,因为大数据的写和读都很消耗性能。
  • 大数据的本地持久化选择Sqlite和Core Data性能更好,虽然两者的性能相似,但是苹果官方更推荐使用Core Data。Core Data运用了ORM思想(对象关系映射),对象的属性与表中的字段自动映射,不需要任何SQL语句,也不需要对表进行操作,是一种纯面对对象的数据库存储操作,类似安卓开发中用到Hibenate。

数据的展示。

一个页面的数据展示最好要限制每一刷新加载数据的条数,不要过多,限制在10-20之内,新数据的展示和老数据的展示,最好是两个不同URL。还有针对页面中UI中有很多的View,在加载的策略上,完全可以采用多线程技术进行同步加载,只把上半部分放在主线程加载,下半部分放到子线中去加载,这样可以大大降低更新数据的时间,当上半部的数据初始化完毕,下半部分就已经在另一个线程中处理完毕,所以这种加载策略更能优化软件性能。

运用算法逻辑

逻辑运算方面的处理要考虑采用做合适的算法处理运算。尽量减少不要运算资源浪费。

在程序中尽量减少使用webview

因为我们程序中webView并不像Safari浏览器加载的那么快,没有相应的硬件支持等。

Autoreleasepool的正确使用

  • 写基于命令行的的程序时,就是没有UI框架,如AppKit等Cocoa框架时。
  • 如果你编写的循环中创建了大量的临时对象;
  • 创建了新的线程。(非Cocoa程序创建线程时才需要)。
  • 长时间在后台运行的任务。

程序瘦身(未更新中)

征求APP优化策略

关于APP性能优化的方法,将在项目实战中持续更新。希望小伙伴们集思广益,都能参与进来,提供更好更完美的优化策略···

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,428评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,016评论 4 62
  • 一阵烟徐徐飘来, 是来自仙境吗? 是来让我回家吗? 是让我不用上学吗? 是梦吗? 不!是雾霾, 雾霾天, 我淡淡的...
    曦景阳晨阅读 205评论 0 0
  • 数组排序 var arr = [9, 4, 6, 3, 2, 8, 4, 0, 4, 56]; //1、冒泡...
    周博通zbt阅读 204评论 0 0