iOS的性能优化

2020-06-10  本文已影响0人  六横六竖亚

1、ipa包体积优化    

1.1 编译配置优化:编译器代码层面优化Optimize Level;Bitcode(较难适配)

1.2 去除符号信息:影响调试和断点,Strip Linked Product,Strip Debug Symbols During Copy(影响第三方库中的符号表配置,注意Cocoapods中的没走xcode所以需要单独用脚本hook配置),Strip Swift Symbols。注:DWARF(源码级别调试文件,比较大),dSYM(独立符号表,包含DWARF)

1.3 支持BitCode:注意所有第三方库都要支持,注意上传包时勾选符号表配置(Include app symbols for your application to receive symboilcated crash logs from Apple)解决崩溃定位问题。

1.4 清理无用代码:1、Dead Code Stripping清理定义但未被使用的代码(OC动态语言中无效,C/C++ swift可以);2、可执行文件的扫描:Mach-O中的__objc_classlist(定义的类)和__objc_classrefs(引用的类)对比,确认后删除定义未引用的类(无工具)。无用方法类似:class selref和menthodList对比。

1.5 Cocoapods中的优化选项:1、每次install都会重置,所以需要hook pod install来设置Pods中每个Target的编译选项;2、podspec导致图片重复拷贝,

1.6 图片资源优化:1、Compress PNG Files和Remove Text Medadata From PNG Files用于压缩和清理PNG图片的无用信息;2、尽量使用asset catelogs(即Assets.xcassets) 管理资源,下载时在不同设备上进行资源切片(slicing)(但无法使用物理路径访问,且.png在sb中会显示不出);3、Fengniao清理无用图片;4、MD5对比图片删除重复资源;5、压缩包资源文件大小排序查找大图手动压缩。


2、app启动优化

按阶段分为 main()加载前;main()加载后到首页启动完成前。

耗时监控:1、Edit scheme -> Run -> Auguments,DYLD_PRINT_STATISTICS = 1统计粗略的耗时;2、Timeprofile + signpost统计详细耗时。

优化main()前:1、减小Mach-O文件大小(和包大小直接相关);2、减少dyld动态链接库的加载(适当合并、适当使用懒加载如直播库,减少rebase和binding,rebase中有耗时的I/O操作);3、缩减runtime进行内存布局的时间(减少类、分类的使用,和selector的数量);4、尽量用+initialize替换+load(耗时操作严禁);5、对Mach-O进行二进制重排,减少内存缺页耗时

优化main()后:主要是在didFinishLaunching中的业务初始化、数据的预加载,以及SDK的加载等,可以考虑移至首页加载后,必须靠前的考虑主线程并发加载。实例:美颜、芝麻信用、地理位置等库的延迟注册。

Mach-O介绍:1、文件结构:ipa包解压后的exec,可执行文件。文件结构:Mach header(CPU架构配置等信息)、Load Commands(二进制文件加载内存的指令,如分配内存,创建线程,代码签名/加密,动态链接库的加载和符号解析等)。2、加载过程:load dyld加载动态链接库(文件位置存在Mach Header中)→Rebasing(修复dyld加载时,随机化内存地址的偏差)→binding(dyld中可能有自己的代码,是符号,则需要进行符号的符号实现的绑定过程)→Objc Setup(类注册、分类加载、方法确定唯一性的过程 / runtime)→Initializers(调用类对象的+load方法)


3、高可用

卡顿检测和分析

主要原因:耗时计算(富文本、Cell算高不缓存),IO操作,主线程死锁等

排查方案:开发和测试过程中,Instruments中的Time Profiler可以很好的统计各个函数耗时,定位卡顿点线上监控:而生产环境的监控则需要从RunLoop入手,检测一个周期的进入和退出的时间间隔(kCFRunLoopBeforeSources和kCFRunLoopBeforeWaiting之间,和kCFRunLoopAfterWaiting之后)来获得主线程的运行情况,当超过设定的卡顿程度则上报。其他方案:CADisplayLink(与屏幕刷新频率同步,通过帧数统计);dispatch_asyn中去ping主线程的响应。

项目中的实例:直播美颜帧数过高;cell算高未缓存;svga动画过大;评论区富文本绘制在主线程等。

崩溃监测和分析

1、NSException异常原因:OC代码导致的Crash监测和上报:NS Get / Set UncaughtExceptionHandler,获取和注册自己的handler,通过NSException自己实现Crash信息的收集、保存和下次上报。

2、Signal信号异常原因:一般是内存问题、重复释放,和系统级的挂起;由iOS底层mach信号异常转换后,抛出的异常。监测和上报:一、signal(SIGxxx,  CustomSignalHandler)函数,进行信号捕获,在CustomSignalHandler中处理信号的信息保存和上报;二、使用sigaction函数进行信号捕获,sigaction(SIGxxx, &action, 0);。注意:捕获信号时,要保存之前别人注册过的handler。常用信号说明(SIG+):SEGV(无效内存),ABRT(自身或外部调用abort()函数),TRAP(断点信号,如try等),BUS(也是内存相关但是总线问题如地址对齐问题)。

3、OOM   

概念:out of memory,低内存崩溃,FOOM是前台内存占用过大引起系统强杀。

原因:内存泄漏,处理大图(解码后生成bitmap的过程很耗内存,建议用imageIO)

监测:Signal捕获Crash的方案无法捕获OOM,要用排除法判定facebook(排除普通崩溃和主动退出,判断是否挂起和处于前后台等)

上报:上报OOM的方案目前可以是在收到警告时记录线程、页面堆栈及其他上下文信息,下次启动,判定有异常退出时上报。

解决:使用适当的图片,及时回收图片;合理使用autoreleasePool;对象按需创建;在出现Memory Warning(内存逐步上涨到临界值时会触发警告,瞬间申请大内存的情况不会触发,如加载巨大的图片)时处理释放内存,也可以一定程度上避免OOM。

作用:优化内存避免OOM有利于后台保活,利于热启动

4、abort() :程序强行终止的函数,无法通过信号收集,一般猜测对可能触发的场景进行监控

5、符号表:即dSYM文件,一个符号名称(代码的文件、行号等)和符号地址(内存)的映射表。符号化:通过dSYM将crash中的内存地址转化成符号信息的过程,对堆栈的解析和还原

常见Crash的防护

方案:网易Baymax运行时自动防护方案

Unrecognized Selector→消息转发机制

KVO注册和移除不匹配→delegate中转存储KVO的info,并swizzle dealloc方法清理这些信息和delegate

NSNotification→hook NSObject的dealloc函数里面removeObserver:self

NSTimer→动态创建桥接Target,不强持有原Target,在合适的时机invalidate

Container(数组字典等)→越界等,用safety方法存取值

野指针→Exception Type: SIGSEGV,Exception Codes: SEGV_ACCERR,使用objc_setAssociatedObject给需要加防护的对象加入野指针标记,释放时走自己的方法

非主线程刷UI→swizzle UIView类中的setNeedsLayout、setNeedsDisplay、setNeedsDisplayInRect等方法(不完全)内判断线程,如过不是主线程直接用dispatch_get_main_queue转移到主线程刷。

上一篇下一篇

猜你喜欢

热点阅读