笔者因为在iOS开发过程当中作过一些优化的工做,对iOS性能优化有一些粗浅的认识,一直想把本身这些经验,简单总结一下。因而最近在工做闲暇时间,准备针对iOS开发的性能优化写一系列文章。web
做为整个系列的第一篇,我打算针对iOS的优化中的一些整体原则作一些总结。由于我以为不管列表流畅度优化也好、启动时间优化也好仍是说其余方面的优化,都有一些共性的原则,只有掌握了这些整体性的原则,才可以更好的作优化,给咱们具体的优化任务指明方向,让咱们少绕弯路。后面若是时间容许,我可能会写一些关于列表流畅度、启动时间和内存优化等方面的文章。算法
在第一篇“优化整体原则”里面。我对优化整体原则总结出包括不要提早过分优化、要找到性能瓶颈、要在不一样性能指标间权衡、要理解优化任务的底层运行机制和要有技术保障体系五大原则,其中具体阐述每个原则的时候并不局限于性能优化方面,会发散到其余的相关领域,会对一些延伸的领域作一些简单的探讨,但愿可以对读者有一些启示。如下是第一篇的主要内容。数据库
这个原则包括优化的过程当中须要避免的两个陷阱,即提早优化和过分优化。swift
提早优化指的是在开发的起始阶段就把性能优化做为一个重要的任务来考虑,在没有实际数据指标的基础上,为了性能提早作的些盲目优化工做。c#
固然这个观点可能会引发争议,由于在某些开发领域,一些性能指标以历史的经验来讲,的确有很大几率甚至必然会有性能瓶颈的问题,所以在架构初期就须要考虑性能的问题。 所以若是把“不要提早优化“这个观点推广全部开发领域上的话,我认为可能不必定合适。可是若是把此观点约束在iOS开发这一领域内,我我的认为仍是成立的。由于在目前阶段iOS平台设备性能广泛较好,苹果不管是硬件层面仍是系统层面对性能方面都作了大量的优化。因此我认为性能方面并非iOS开发过程当中须要首要考虑的因素。相比性能, 我我的认为在iOS开发的初始阶段,如下几个方面是更重要的,是须要首先考虑的。设计模式
首先须要考虑的是架构的选择,这里的架构指的是Native架构、web架构、Native和web混合架构和跨平台的架构。这里面我我的的意见是首先应该尽可能避免使用web架构,从Facebook早期的失败经验能够看出,web和Native相比的确存在诸多性能、体验等方面的问题。连大厂都没法完全改善webview的问题,况且咱们。可是在一些和用户体验相比,对动态化需求更加迫切的应用场景下,是能够选择web架构的,好比你们都一直在吐槽某铁路售票软件。Native架构的优点是产品体验好,对大多数iOS开发者技术栈友好,缺点是因为苹果对热更新作了严格限制,致使一些动态化的方案没法使用。Native和web混合架构主要是在Native架构上,在一些运营需求十分强烈的场景下(如电商等场景),某些模块使用web开发,这样既能够在App大部分场景下使用Native架构,保证用户体验,又知足了部分场景动态化的需求。跨平台的架构主要是能够减小多端开发的成本,使用一套代码完成iOS和Android两个平台的开发,目前主流的框架有ReactNative、Weex和Xamarin等。这些跨平台架构的愿景都很美好,但实际使用过程当中,我的以为现阶段并不比使用Native架构节省人力,其中会遇到许多已知的未知的坑,固然做为新的技术咱们应该持开发的心态,但在使用时候也须要全面的评估,尤为做为一个可能有很长生命周期的应用,在使用非官方推荐的开发架构也好、开源库也好,若是后期无人维护的话,本身团队是否是有实力去接盘,若是不能,使用苹果官方推荐的技术栈则更稳妥些。缓存
其次须要考虑的是开发语言的选择,这个方面其实在选择了架构以后,也将可选的开发语言范围缩小到几个。并且实际项目开发中并不难选,由于团队开发人员的技术栈几乎决定了使用的开发语言,若是使用了大部分团队成员都不熟悉的语言,我相信即便所选语言在不少方面都有压倒性的优点,项目的推动也不会十分顺利。可是排除团队技术栈的因素,不一样的开发语言的确是各有千秋。你们都知道iOS开发过程当中,若是使用Native架构,官方的开发语言是objc和swift。objc是早期iOS开发的官方推荐语言,优势是其动态性,十分灵活,能够实现许多“黑魔法”,缺点语法略怪异(固然对于iOS开发者,使用久了也不以为怪了),另外是对一些高级的语言特性支持的不是很好(记得最初使用objc开发iOS应用的时候,由于一些特殊的需求。因为objc不支持namespace,给团队形成了很大的困扰)。swift是苹果近年来主推的开发语言, 其吸取了许多其余语言的先进特性,也比较容易上手。关于两种开发语言的具体技术细节,你们有兴趣能够本身查看一些资料了解下。虽然苹果一直在力推swift,可是目前在国内iOS开发领域,因为一些用户基数大的主流App,均是在swift出现前使用objc编写的,并且大多通过了数年的版本迭代,加之早起swift ABI的不稳定和版本之间升级须要较多工做,还有swift和objc混编的一些问题,致使目前国内主流App大多仍使用objc做为开发语言。在一些创业公司,或者新的项目中,才有部分开发者使用swift,固然若是目光放长远的话,将来必定是swift的天下,这两年objc在每一年的语言排名中逐年降低也侧面印证了这一点。 除了官方推荐的objc和是swift以外,若是使用跨平台等其余架构,还可使用如js、c#等语言,有兴趣的能够自行了解下。安全
再次须要考虑的是开发过程当中具体的代码架构的选择,这里只简单谈谈Native架构下的代码架构选择。 目前iOS开发中经常使用的架构有MVC 、MVVM、VIPER、MVP等。关于这些架构,网上目前有不少的介绍,你们若是对具体细节有兴趣能够自行查阅。这里我只想补充一点,你们在学习和实践时,不要盲目跟风新技术,好比MVVM等架构未必比MVC好不少,MVC也未必是一个过期的框架。要知道不少新架构带来的扩展性和解耦行都是经过引入间接层来实现的,随之而来的多是更多的胶水代码和更复杂的代码结构。但愿你们在选择的时候可以根据项目的特色和团队自身的情况,选择最适合本身团队和项目的代码架构。性能优化
除了上面说的三点,还有一些其余的关键点须要你们在项目初期考虑,好比如何在团队内部达成统一的代码风格?一些关键的技术如何选型?如何保证代码结构清晰、简单、扩展性好等等。性能问题能够在项目后期开始考虑,若是真的发现明显的性能问题再优化也来得及。好比项目一开始凭直觉感受某一个模块可能会有性能问题,就盲目使用多线程,而不是根据实际状况具体问题具体分析。会致使程序复杂且容易出现线程安全问题。多线程
过分优化是指为了优化性能,过分增长系统复杂度和维护成本,使得开发周期变长。虽然可能性能上带来了必定的提高,可是和过分优化而致使的这些缺点来比,这么作显而易见是得不偿失的。
笔者在工做过程当中,发现许多同窗在性能优化过程当中,都容易陷入这种过分优化的陷阱。好比这一个简单的设置界面,一共只有十几个静态的cell, 若是去考虑圆角性能、离屏渲染、图片缓存、高度缓存、异步渲染甚至缓存布局信息,这些无疑是陷入了过分优化的陷阱,在这个应用背景下,简单快速的实现功能才是第一要务。在目前苹果的开发框架和平台上,通常若是出现性能问题,以个人实际经验来讲,问题大部分是出在业务逻辑上面,因此遇到问题首先须要在业务逻辑上找问题,一些过分的极限的优化,彻底是没有必要的。
其实不光是性能优化,我发现许多同窗在平常开发中,到处都有过分设计的状况。好比设计模式中的design happy这一陷阱,许多初学者在刚开始学习设计模式的时候,十分痴迷设计模式在解决不一样问题时,对代码的解耦性和可扩展性上的威力,在开发过程当中会时时刻刻想着应该用什么设计模式。结果致使不少的过分设计,其实咱们写代码过程当中,若是能遵照基本的SOLID原则,大部分状况下就能够写出高质量的代码。
另一个例子是组件化。近期iOS组件化是一个十分流行的话题,有许多团队提出了不一样的组件化方案。实际项目中,团队在是否采用组件化方式开发的选择上,我但愿要结合项目特色和团队组织架构形式具体问题具体分析,不要盲目跟风。在产品功能相对单1、开发人员较少、并行开发需求不强烈的状况下,推行组件化,不但增长系统复杂度,并且增长开发人员学习成本高,使得开发成本变大,我我的以为这种规模的应用初期须要更多考虑的是如何快速上线、快速迭代和保证App质量。所以若是可以进行清晰的分层,严格遵照简单统一的架构模式便可。组件化比较适合从功能形态上能够清晰划分若干模块的产品,好比美团、58同城、淘宝和携程等产品,内部有多个业务模块,并且这些公司开发此类“航母”App的时候,会从组织架构把不一样业务划分给不一样的开发团队,为了可以保证不一样团队之间可以独立并行开发和发版,最大程度上减小代码的依赖程度,这个时候应用组件化则是最佳实践。
上面是对不要提早过分优化原则的详细阐述,并引伸到相关开发领域,作了一些不成熟的探讨。下面介绍性能优化整体原则的第二个。
在作优化前,必定要首先找到性能瓶颈有哪些,依性能严重程度逐个解决。不要盲目优化,不然最后可能花了很大的力气,优化掉的可能只是性能损耗很小的一部分。这一原则我以为尤其重要,由于我在工做中碰见过包括我在内,不少不进行性能瓶颈查找,全凭主观猜想进行性能优化的状况。 在寻找性能瓶颈过程当中,也须要注意如下问题。
不要主观猜想,让性能评测数听说话。
这一点十分重要,要时刻记住要以事实说话,不要觉得某个函数使用的算法的时间复杂度是O(n2)就以为必定会有性能问题,非要费很大力气优化到O(n*logn), 却不知你的输入数据可能只有几十或者几百个,即便O(n2)也不会有多大的性能问题。也不要觉得某个方法仅仅调用了系统库的一个简单get方法,就不会有什么性能问题,却不知这个get方法里可能包含一些十分耗时的操做(好比磁盘IO)。所以在遇到性能问题的时候,必定不要凭主观猜想,实地跑一下性能数据,让数据告诉咱们性能瓶颈究竟在哪里。
要使用恰当的性能评测工具。
对于开发版本的性能优化,Xcode提供的instruments绝对是最好的寻找性能瓶颈的工具,没有之一。instruments有丰富的性能评测工具,包括经常使用的Core Animation、Time Profiler、Leaks和Allocations等等。这些工具在分析函数执行时间、fps和内存等方面给我提供了十分便捷的功能。在使用instruments过程当中须要注意:
要使用真机,而不是模拟器。模拟器的CPU比iOS机器要快不少,因此在模拟器上,CPU相关的操做会更快。由于Mac的GPU和iOS设备上的GPU不一样,因此模拟器须要在CPU上经过软件去模拟iOS设备上的GPU,因此GPU相关的操做会更慢。所以若是使用模拟器去进行性能优化的话,评测设备和真实用户设备性能表现的不一致,会致使优化的效果大打折扣。这里面内存是一个例外,在作内存优化的时候,使用模拟器和真机通常差异不大,可使用模拟器进行内存的优化。
要使用Release配置而不是Debug配置,由于在release包的时候,编译器会作一些优化以提升性能。本身的工程代码可能也会在release下作一些优化,好比去除log信息和一些debug功能等。咱们关心的是release下的性能,由于用户最终也是使用的release的安装包。因此测试的时候要必定要记住在release配置下进行,instruments进行性能评测的时候,默认是在release下进行的。可是工程代码里面的优化则须要本身注意。笔者就曾经为此付出过很大代价,由于没有注意工程代码里面的一些debug功能,致使优化过程当中错误的认为动态库是影响启动时间的罪魁祸首,花了很大力气把动态库修改成静态库,白白浪费了不少时间。
要使用性能相对差的机器进行评测,由于咱们须要保证的是咱们的应用在性能差的机器上也有良好的表现。若是有条件,最好可以覆盖多个机型,和咱们传统上的认识不一样,机型越新性能不必定越高,例如iPad3在动画和渲染性能上比iPad2差。
要覆盖不一样系统版本,由于在iOS系统上,通常系统版本越高,同一机器性能大致上趋于差,因此若是只覆盖低版本的机型,可能在高版本上表现的性能会不尽如人意。
除了使用instruments,还可使用log等方式进行查找性能瓶颈。
对于线上的App,查找性能瓶颈的工具主要是Application Performance Management(APM)。目前各大公司基本都有本身的APM,主要是对线上App进行性能监控以及预警。由于在开发和测试阶段,因为使用人数相对比较有限,很难覆盖全部的业务场景。而在App发布出去后,大量用户在使用过程当中的性能表现,会给咱们的App带来更全面的性能评测数据,所以线上App的性能监控是十分重要的。
要抓重点,有的放矢。找到性能损耗大的前N个问题,依重要程度和解决的难易程度解决,这样才能花最少的精力,解决最大问题。
在已经找到性能瓶颈的时候,解决性能问题的方法则须要具体问题具体分析,要在不一样性能指标间权衡,以达到整体最优。
这须要咱们要有总体的意识,App的性能能够分为不少类,不一样的性能指标对用户体验形成的影响也不尽相同,好比fps主要影响的是用户的滑动体验,页面加载时间和应用启动时间影响的是用户等待时间上的体验。咱们在优化的过程当中,要牢记咱们的目标是但愿App的总体体验最优,而不是某一单项的性能指标最优。不一样优化指标之间多是呈正相关,好比优化了滑动过程当中大量函数的耗时时间,使得fps性能提高,可能会致使App的耗电量变少。同时,不一样优化指标也多是负相关、相互制约的,好比为了流畅性作了过多的cache,会致使内存性能降低,甚至致使由于memory warning致使被系统kill掉,这无疑对App的总体体验形成了负面的影响。所以实际优化过程当中须要咱们反复权衡利弊和取舍,达到总体的性能最优。
若是不理解优化任务的底层运行机制,可能很难达到更好的优化效果。
好比在作启动时间优化的时候,若是你不知道iOS中App的启动时间是由main以前和main以后两部分时间组成的,此时若是你的App是由于main函数以前的部分占用了过多的启动时间,可能你花了大量的精力去优化main以后的时间却没有达到好的优化效果。若是你不知道App启动过程的运行机制,就没法知道去检查是否连接了过多的自定义的动态库或者去load函数里面确认是否有耗时的操做等等。还有在作fps优化的时候,若是不了解卡顿的底层缘由是什么、一个view从建立到显示过程当中经历那些步骤、CPU和GPU在这个过程当中都扮演什么角色,则很难作到丝滑般的顺畅?还有在作内存优化的时候,若是不了解内存分为哪几类、系统对App和不一样类型extension的内存限制机制的不一样、超过限制系统会采起什么操做等等,也很难把内存优化作好。所以只有深刻了解底层机制才能更好的有针对性的提出更优的解决方案。
其实不仅是在性能优化方面,在开发过程当中不少状况下,了解底层的原理会让你变得更高效,更容易解决遇到的各类问题。在这里分享一个我印象比较深的一次经历。记得有一次在开发某个功能的时候,须要用到level db数据库,在开发过程当中作单元测试的时候发现,level db的本地存储文件在不断删除和写入的过程当中,越变越大,甚至达到1G大小。当时第一印象觉得是在使用上出了问题,因此上层业务逻辑上查找问题,结果查了好久都没有找到问题。但若是我在使用level db的时候去多了解一下其底层的实现原理,了解LSM(Log-Structured-Merge Tree)的原理,遇到这个问题的时候就不会认为这是一个bug,也不会浪费了大把的时间来作无用功。因此建议你们不要抱怨天天的工做过于简单枯燥,在开发过程当中多去挖掘一些深层次的东西,不但让你的技术的深度不断加深,也会对你的编码效率有很大的提高。
性能优化不能一劳永逸,我我的以为更是一场持久战。不只须要你可以在某个特定时期作专项优化的攻坚战,还要为打好持久战作出好的后勤补给,为了能使App长期保持好的性能,不只仅须要开发人员有良好的开发技能,还须要有一些技术保障和体系。下面简单罗列我能想到的几个方面。
要有好的测试保障,这里的测试保障不只仅指的是测试人员的手动测试,更须要的自动化测试。要创建针对不一样性能指标的专项自动化测试,创建一套从定时运行测试到测试结果的输出等一套完整的自动化测试体系,可以为性能的保证提供坚实的数据支撑。
引入关键性能指标上线准入制度。在开发阶段,为了避免将有性能问题的代码带到线上,能够将好比启动时间、FPS、安装包大小等指标做为关键指标,上线前进行自动化测试,如指标不达标,不容许上线。
对于线上App使用APM进行监控,发现线上的性能问题,有及时的预警机制,可以随时解决线上的性能问题。
开发过程当中,代码中须要有对性能保障的设计。 好比能够设计可复用的高性能控件,这样其余开发人员在开发相似功能时,能够简单复用,不只提高了性能,并且大大节省了开发的时间。还有好比为了防止App随着版本迭代致使启动时加载的服务越来约多,致使启动变慢,能够设计App启动器,把这些任务统一放到主界面加载完成后再执行,而且在组内开发人员中造成硬性的规范,但凡启动时期没必要须的服务,要么不要执行,要么统一放到启动器的主界面加载完成的回调中执行。
按期作一些性能优化方面的技术分享,不只仅能够提升组内同窗的开发技能,还能够活跃组内的技术气氛。
以上我对iOS性能优化的整体原则作的总结,但愿可以对你们有一点点启示。其中可能不少想法并不成熟,也但愿你们可以多多批评互相探讨,共同进步。