在屏幕成像的过程中,CPU和GPU起着至关重要的作用。
垂直同步信号(VSync)
表示将要显示一帧画面(一屏幕);水平同步信号(HSync)
表示一行一行显示,最后一行执行完又会发送一个垂直同步信号(VSync)显示下一帧画面。github
上有一个第三方库LXDAppFluecyMonitor可以检测每个执行方法消耗的时常。冷启动(Cold Launch)
:从零开始启动APP;热启动(Warm Launch)
:APP已经在内存中,在后台存活着,再次点击图标启动APP。(Edit scheme -> Run -> Arguments->Environment Varlables)
,DYLD_PRINT_STATISTICS
设置为1,如果需要更详细的信息,那就将DYLD_PRINT_STATISTICS_DETAILS
设置为1。
可以看见控制台打印启动过程各个阶段所占时间长及比例。
APP的冷启动可以概括为3大阶段:dyld、runtime、main。
dyld(dynamic link editor),Apple的动态链接器,可以用来装载Mach-O文件(可执行文件、动态库等),启动APP时,dyld所做的事情有:装载APP的可执行文件,同时会递归加载所有依赖的动态库
当dyld把可执行文件、动态库都装载完毕后,会通知Runtime进行下一步的处理。
启动APP时,runtime所做的事情有:调用map_images进行可执行文件内容的解析和处理
在load_images中调用call_load_methods,调用所有Class和Category的+load方法,进行各种objc结构的初始化(注册Objc类 、初始化类对象等等),调用C++静态初始化器和__attribute__((constructor))修饰的函数,
到此为止,可执行文件和动态库中所有的符号(Class,Protocol,Selector,IMP,…)都已经按格式成功加载到内存中,被runtime 所管理。
APP的启动 - main:调用UIApplicationMain函数,加载完成后会调用AppDelegate的application:didFinishLaunchingWithOptions:方法。
总结一下
APP的启动由dyld主导,将可执行文件加载到内存,顺便加载所有依赖的动态库;
并由runtime负责加载成objc定义的结构;
所有初始化工作结束后,dyld就会调用main函数;
接下来就是UIApplicationMain函数,AppDelegate的application:didFinishLaunchingWithOptions:方法。
dyld
:减少动态库、合并一些动态库(定期清理不必要的动态库);减少Objc类、分类的数量、减少Selector数量(定期清理不必要的类、分类);减少C++虚函数数量;Swift尽量使用struct。untime
:用+initialize方法和dispatch_once取代所有的__attribute__((constructor))、C++静态构造器、ObjC的+load。main
:在不影响用户体验的前提下,尽可能将一些操作延迟,不要全部都放在finishLaunching方法中你在项目中是怎么优化内存的?
用ARC管理内存、在正确的地方使用reuseIdentifier、当View设置为透明的时候,一般把apaque设为NO,减小开销,尽量不要离屏渲染,对内存有好处、避免庞大的XIB、不要阻塞主线程、懒加载、Cache 缓存等等。
列表(UITableView)卡顿的原因可能有哪些?你平时是怎么优化的?
- 最常用的就是cell的重用, 注册重用标识符;
如果不重用cell时,每当一个cell显示到屏幕上时,就会重新创建一个新的cell
如果有很多数据的时候,就会堆积很多cell。
如果重用cell,为cell创建一个ID,每当需要显示cell 的时候,都会先去缓冲池中寻找可循环利用的cell,如果没有再重新创建cell- 避免cell的重新布局
cell的布局填充等操作 比较耗时,一般创建时就布局好
如可以将cell单独放到一个自定义类,初始化时就布局好- 提前计算并缓存cell的属性及内容
当我们创建cell的数据源方法时,编译器并不是先创建cell 再定cell的高度,而是先根据内容一次确定每一个cell的高度,高度确定后,再创建要显示的cell,滚动时,每当cell进入凭虚都会计算高度,提前估算高度告诉编译器,编译器知道高度后,紧接着就会创建cell,这时再调用高度的具体计算方法,这样可以方式浪费时间去计算显示以外的cell- 减少cell中控件的数量
尽量使cell得布局大致相同,不同风格的cell可以使用不用的重用标识符,初始化时添加控件,不需要使用的可以先隐藏;- 不要使用ClearColor,无背景色,透明度也不要设置为0
渲染耗时比较长- 使用局部更新
如果只是更新某组的话,使用reloadSection进行局部更- 加载网络数据,下载图片,使用异步加载,并缓存
- 少使用addView 给cell动态添加view
- 按需加载cell,cell滚动很快时,只加载范围内的cell
- 不要实现无用的代理方法,tableView只遵守两个协议
- 缓存行高:estimatedHeightForRow不能和HeightForRow里面的layoutIfNeed同时存在,这两者同时存在才会出现“窜动”的bug。所以我的建议是:只要是固定行高就写预估行高来减少行高调用次数提升性能。如果是动态行高就不要写预估方法了,用一个行高的缓存字典来减少代码的调用次数即可
- 不要做多余的绘制工作。在实现drawRect:的时候,它的rect参数就是需要绘制的区域,这个区域之外的不需要进行绘制。例可以用CGRectIntersectsRect、CGRectIntersection或CGRectContainsRect判断是否需要绘制image和text,然后再调用绘制方法。
- 预渲染图像。当新的图像出现时,仍然会有短暂的停顿现象。解决的办法就是在bitmap context里先将其画一遍,导出成UIImage对象,然后再绘制到屏幕;
- 使用正确的数据结构来存储数据。
参考:
iOS 如何优化项目
在iOS中如何优化内存(一)
INSTRUMENTS调试工具的使用(一)
遇到tableView卡顿嘛?会造成卡顿的原因大致有哪些?TableView的性能优化