本篇主要记录一下我对界面优化、时间优化和耗电优化、安装包瘦身上的一些探索。我尽可能按照本身的理解来进行描述,若有不当,欢迎指正。php
## 1、界面优化html
### 一、卡顿原理ios
要了解卡顿原理,须要对帧缓冲区、垂直同步、CPU 和 GPU 几个词进行一下了解,而后综合起来,就能够获得卡顿的答案。git
#### 1.一、帧缓冲区github
听起来很高大上,其实就是用来存放每一帧画面数据的一个 “仓库”,一个仓库只存放一帧画面的数据,iOS 一直是双缓存,就是有两个仓库,存当前帧数据的叫 “正式仓库”,存下一帧数据的叫 “预备仓库”。缓存
当正式仓库的数据被取走后,二者身份交换,原来的预备仓库转正为正式仓库,原来的正式仓库变成预备仓库。性能优化
#### 1.二、垂直同步 (VSync)网络
就是一个“信号”,通知 APP 该开始准备往预备仓库里存放数据了,系统过一会就要来取,这个时间大概是 16.7 毫秒。架构
#### 1.三、CPU (中央处理器)app
主要的工做有:正式对象的建立和销毁、对象属性的调整、布局计算、文本的计算和排版、图片的格式转换和解码、图像的绘制。咱们能够理解为负责包裹内部的处理工做,简称 “打包”。
#### 1.四、GPU (图形处理器)
主要的工做有:将 CPU 计算好的内容进行变换、合成、渲染等处理,而后将渲染结果提交到帧缓冲区。咱们能够理解为,对 CPU 给过来的包裹进行分类、排列等操做后,存放到仓库里去,简称 “入库”。
#### 1.五、卡顿原理
当收到系统发过来的 VSync “信号”后,CPU 就开始对这一帧画面的数据进行 “打包”,而后交给 GPU 进行 “入库” 操做,存入到 “预备仓库” 中。
16.7 毫秒后,预备仓库转正,系统开始读取仓库里的数据,若是这个时候,仓库中的数据尚未准备好,那么系统就会大发雷霆,放弃读取仓库中的数据。
那么这个时候,由于系统的宁缺毋滥,致使了显示器上显示的仍是上一帧画面,就形成了卡顿的效果。
### 二、优化
做为软件开发工程师的咱们,既不能延长 16.7 毫秒的处理时间,也不能改变系统的脾气,那咱们能作的就是尽可能在这个时间内完成数据的准备。要么 “打包” 快一点,要么 “入库” 快一点,也就是针对 CPU 和 GPU 的工做进行优化,这就是性能优化的工做了。
#### 2.一、CPU 工做之正式对象的建立和销毁
- UITableViewCell 和 UICollectionViewCell 的复用,能够减小 cell 的建立操做;
- 尽可能使用轻量级的对象,能够减小对象的建立时间,好比在不须要事件处理的场景里,使用CALayer 比 UIView 会更加合适;
- 表情键盘使用 UICollectionViewCell 代替 UIButton,能够减小对象的建立操做;
- 对象不涉及UI操做,放到后台线程建立;
- 性能敏感的界面,storyborad 的资源消耗>代码建立;
- 推迟对象建立的时间,对象放到多个任务中,好比懒加载;
#### 2.二、CPU 工做之对象属性的调整
- UIView 有一个 CALyer 的属性,UIView 负责事件的处理,CALyer 负责图层的绘制和显示。须要注意的是,CALyer自己是没有属性的,因此当改变 UIView 的显示相关的属性如 frame、bounds 和 transform 的时候,会消耗较多的资源,因此减小对这些属性的一些没必要要修改,能减少 CPU 的压力;
#### 2.三、CPU 工做之布局计算
- UITableViewCell 高度提早计算并存储,要用的时候直接读取;
- frame 计算好,减小没必要要的修改;
- Autolayout 会比直接设置 frame 消耗更多的 CPU 资源;
#### 2.四、CPU 工做之文本的计算和排版
- 普通文本能够在子线程用 [NSAttributedString boundingRectWithSize:options:context:] 来计算文本宽高,用 -[NSAttributedString drawWithRect:options:context:] 来绘制文本;
- CoreText 对象占用内存较少,当显示大量文本时,能够用 CoreText 对文本异步绘制;
#### 2.五、CPU 工做之图片的格式转换和解码
- 在后台线程先把图片绘制到 CGBitmapContext 中,而后从 Bitmap 直接建立图片;
#### 2.六、CPU 工做之图像的绘制
- UITableViewCell 滑动减速的时候才加载图片,能够参考这个[demo](https://github.com/johnil/VVeboTableViewDemo);
- 加载图片时,imageNamed 方法默认加载图片成功后会内存中缓存图片,下次读取会很快;imageWithContentsOfFile 方法不会缓存图片,大图片可使用该方法;
#### 2.七、GPU 工做之渲染
- 尽可能不要让图片和视图的大小超过 GPU 纹理尺寸上限:4096 × 4096,否则图片还须要通过 CPU 的处理;
- 尽可能减小视图数量和层次,并设置视图为不透明,UIView 的不透明属性 (opaque) 默认为 YES,通常设置背景颜色便可;CALayer 的不透明属性 (opaque) 默认为 NO,须要设置为 YES;
- CALayer 的 border、圆角、阴影、遮罩,一般会触发离屏渲染,尽可能少用,圆角属性可使用 CoreGraphics 绘制或使用圆角图片代替,关于离屏渲染的知识,具体能够参考这篇文章:[iOS 图形性能优化](http://www.cocoachina.com/cms/wap.php?action=article&id=25543);
- 图片的 size 最好恰好跟 UIImageView 的 size 保持一致,这涉及到像素对齐的知识,也能够在上面这篇文章中详细了解;
## 2、时间优化
要谈论时间优化,就要先了解程序启动的过程和耗时的缘由,而后针对性的进行优化。
### 一、程序启动过程
程序的启动分为冷启动和热启动两种模式,其中冷启动是从程序被杀死后加载起来的过程,热启动是从后台到前台的过程。相比之下,热启动是包含在冷启动里,而且比冷启动少了部分加载过程的,因此,咱们日常说的启动优化,通常都是针对冷启动的。
从点击程序的图标,到首页渲染完成显示到用户眼前,主要有三个阶段。
#### 1.一、阶段一:main 函数以前
该阶段主要进行动态连接库 (dylib) 和自身 App 可执行文件的加载。
其中动态连接库包括:iOS 中用到的全部系统 framework,加载 OC runtime 方法的 libobjc,系统级别的 libSystem,例如 libdispatch(GCD) 和 libsystem_blocks (Block)。
#### 1.二、阶段二:main 函数到首页加载以前
该阶段主要执行 main 函数到 applicationWillFinishLaunching 方法结束。
#### 1.三、阶段三:首页开始加载到渲染完成
该阶段主要执行首页界面 viewDidLoad 方法和 UITabBarController 第一个子控制器 viewWillAppear 里的代码。
### 二、耗时产生缘由
#### 2.一、阶段一里可能产生耗时的有:
- 加载大量动态连接库;
- 注册大量 Objc 类 、初始化类对象 (Objc 的 +load 方法);
- 加载大量分类里的方法;
- 加载大量 C++ 静态对象;
- 执行大量声明为 __attribute__((constructor)) 的C函数。
#### 2.二、阶段二里可能产生耗时的有:
- 在 applicationWillFinishLaunching 执行了 UITabBarController 以及 子控制器的建立,并在 viewDidLoad 方法里执行了大量的耗时操做;
- 大量第三方应用的配置和启动项的累积;
#### 2.三、阶段三里可能产生耗时的有:
- 在 UITabBarController 第一个子控制器的 viewWillAppear 方法里执行了大量的耗时操做;
### 三、启动时间优化
#### 3.一、针对阶段一的优化:
- 减小非系统库的依赖、合并不是系统库,苹果最多支持6个非系统的动态库合并为一个;
- 按期清理项目里不使用的类和方法,检测工具可使用AppCode,关于AppCode的使用请参考这篇文章: [AppCode使用介绍](https://www.jianshu.com/p/6be7dc65d67e);
- 将没必要须在 +load 方法中作的事情延迟到 +initialize 中,关于两者的区别能够参考[这篇文章](https://ityongzhen.github.io/iOS中load和initialize.html);
- 减小分类和分类里方法的数量;
- 尽可能不要用 C++ 虚函数;
- 删减一些无用的静态变量。
#### 3.二、针对阶段二的优化:
- 对启动项和第三方应用配置根据优先级区分,部分不须要在程序启动就初始化的配置,进行延后处理;
- 减小在 viewDidLoad 里的耗时处理;
#### 3.三、针对阶段三的优化:
- UITabBarController 第一个子控制器里的一些耗时操做能够放到 viewDidAppear 方法中,先把界面加载出来,而后再拿到数据刷新界面;
- 增长广告页,在这个时间内准备好首屏页面和数据;
### 四、耗时检测工具
- Xcode自带的 Time Profiler,具体操做能够参考[这篇文章](https://blog.csdn.net/lichuandev/article/details/79636363);
- 查询main函数以前的耗时:菜单:Product->Scheme->Edit Scheme->Environment Variables,设置:key:DYLD_PRINT_STATISTICS ,value:1。
## 3、耗电优化
要研究耗电优化,就要先明白耗电产生的缘由,而后针对性的作出一些优化。根据耗电的缘由和可优化类型,能够分为 CPU 和 GPU 操做优化、网络优化、定位优化、动做传感器优化和蓝牙优化五大类。
### 一、CPU 和 GPU 操做优化
CPU 和 GPU 消耗是全部开发者绕不开的难关,良好的开发习惯,能让咱们减小不少能耗,这里能够将前面介绍的界面优化结合起来,除此以外,有几个地方能够进行优化:
- 少用定时器,每次用的时候都仔细思考下:首先,能不能用其余方式代替,好比定时刷新数据改成触发式刷新,还有[dispatch source代替定时器监测文件变化](https://www.jianshu.com/p/ccde42d839b8);若是不能代替,那么注意的是要设置一个合适的超时时间,以及在再也不须要时及时关闭重复性定时器。
- 文件的读写操做很耗电,咱们要尽可能减少读写操做的频率,思路有:
小数据和小改动最好批量一次性写入;使用 SQLite 或 Core Data 存储大量的数据;
### 二、网络优化
- 使用断点续传,不然网络不稳定时可能屡次传输相同的内容
- 减小、压缩网络数据
- 让用户能够取消长时间运行或者速度很慢的网络操做,设置合适的超时时间
- 网络不可用时,不要尝试执行网络请求
- 批量传输,好比,下载视频流时,不要传输很小的数据包,直接下载整个文件或者一大块一大块地下载。若是下载广告,一 次性多下载一些,而后再慢慢展现。若是下载电子邮件,一次下载多封,不要一封一封地下载
### 三、定位优化
- 不少 APP 为了记录用户的活动或者提供基于位置的服务会进行定位。定位精度越高,定位时间越长,消耗电量也就越多。因此 APP 应该尽可能下降定位精度、缩短定位时间。不须要位置信息以后当即中止定位。
- 若是只是须要快速肯定用户位置,最好用 CLLocationManager的requestLocation 方法。定位完成后,会自动让定位硬件断电
- 若是不是导航应用,尽可能不要实时更新位置,定位完毕就关掉定位服务
- 尽可能下降定位精度,好比尽可能不要使用精度最高的 kCLLocationAccuracyBest
- 须要后台定位时,尽可能设置 pausesLocationUpdatesAutomatically 为 YES,若是用户不太可能移动的时候系统会自动暂停位置更新
- 尽可能不要使用 startMonitoringSignificantLocationChanges,优先考虑 startMonitoringForRegion
### 三、动做传感器优化
- 用户移动、摇晃、倾斜设备时,会产生动做(motion)事件,这些事件由加速度计、陀螺仪、磁力计等硬件检测。在不须要检测的场合,应该及时关闭这些硬件
- 长时间用不上加速度计、陀螺仪、磁力计等设备的动做数据时,应该中止更新数据,否则也会浪费电能。应按需获取,用完即停。
### 四、蓝牙优化
- 没有必要的时候不要扫描蓝牙外设。
- 扫描外设时通常不要用 CBCentralManagerScanOptionAllowDuplicatesKey。
- 只查找你须要的外设服务。外设可能提供不少服务和特性(characteristic),查找外设的时候能够指定 UUID。
- 不要轮询设备特性值,用通知监测特征值的变化。
- 特性值再也不提供通知或者再也不须要通讯的时候就断开链接。
### 五、耗电检测
- 在Xcode中选择View > Navigators > Show Debug Navigator,这里提供了不少仪表用于分析功耗。Energy impact能够查看正在运行的app的功耗
- 启动 Instruments,选择你的设备和要检测的 APP,打开 Energy Log 进行检测
- 设备上进入设置 > 开发者 > Logging > 开启功耗记录。注意:若是手机里没有开发者选项请看[这里](https://www.jianshu.com/p/a1d075b3472c)
## 4、安装包瘦身
安装包的大小受资源文件和可执行文件影响,因此针对性的优化也是这两方面。
### 一、资源文件优化
- 在项目中引入图片时候,直接在 Assets.xcassets 中添加就能够,这样能使用到 App Slicing 功能,这样当用户从 App Store上下载 App 时,能够只下载适用于其设备的 App 架构版本和所需资源,从而减小App所占的空间,且如今基本没有 1x 屏幕的设备了,因此能够不用提供这个分辨率的图片。
- 使用 [LSUnusedResources](https://yq.aliyun.com/go/articleRenderRedirect?url=https%3A%2F%2Fgithub.com%2Ftinymind%2FLSUnusedResources%2F) 查找无用图片
- 使用 [TinyPNG](https://yq.aliyun.com/go/articleRenderRedirect?url=https%3A%2F%2Ftinypng.com%2F)有 损压缩图片
- 启动图片通常较大,能够用 LaunchScreen.storyboard 替换
- 音频文件、视频文件和 H5 远端化
### 二、可执行文件优化
- 清理无用代码--[AppCode](https://www.jetbrains.com/objc/),用它的 inspect code 来扫描无用代码,包括无用的类、函数、宏定义、value、属性等
- 去掉异常支持,Enable C++ Exceptions、Enable Objective-C Exceptions 设置为 NO, Other C Flags 添加 -fno-exceptions
- 在 release 状态下,Strip Debug Symbols During Copy、Strip Linked Product、Make String Read-Only、Dead Code Stripping、Deployment PostProcessing、Symbols hidden by default 设为Y ES,Optimization Level 设置为 Fastest、Smallest
- OC项目中使用 Swift,会增长安装包大小,由于 FrameWork 中会加入为了支持 Swift 的动态库集合,若是纯 Swift 项目,不会引入这些东西。
- 删除不使用的三方库,功能用的少可是体积大的三方库能够考虑本身重写,合并功能重复的三方库
最后郑重声明,本篇里只是记录一下个人我的总结,资料大量参考了如下文章:
[iOS 保持界面流畅的技巧](https://blog.ibireme.com/2015/11/12/smooth_user_interfaces_for_ios/)
[今日头条iOS客户端启动速度优化](https://techblog.toutiao.com/2017/01/17/iosspeed/#more);
[iOS App 启动性能优化](https://mp.weixin.qq.com/s/Kf3EbDIUuf0aWVT-UCEmbA);
[iOS App冷启动治理:来自美团外卖的实践](https://mp.weixin.qq.com/s/jN3jaNrvXczZoYIRCWZs7w);
[iOS app启动速度研究实践](https://zhuanlan.zhihu.com/p/38183046?from=1086193010&wm=3333_2001&weiboauthoruid=1690182120);
[iOS进阶--App功耗优化看这篇就够了](http://www.cocoachina.com/articles/21428);
[iOS的性能优化](https://www.jianshu.com/p/49cecc06e99f);
[iOS安装包瘦身小记](https://www.codercto.com/a/69108.html);
[iOS App 安装包瘦身指南](https://yq.aliyun.com/articles/226555?utm_content=m_33409)
欢迎你们来[个人小窝](https://zmfflying.github.io/)作客啊,里面记录下了我进步的点点滴滴,一切逆境只是前进的理由,与君共勉。