筆者由于在iOS開(kāi)發(fā)過(guò)程中做過(guò)一些優(yōu)化的工作,對(duì)iOS性能優(yōu)化有一些粗淺的認(rèn)識(shí),一直想把自己這些經(jīng)驗(yàn),簡(jiǎn)單總結(jié)一下。于是最近在工作閑暇時(shí)間,準(zhǔn)備針對(duì)iOS開(kāi)發(fā)的性能優(yōu)化寫一系列文章。
作為整個(gè)系列的第一篇,我打算針對(duì)iOS的優(yōu)化中的一些總體原則做一些總結(jié)。因?yàn)槲矣X(jué)得無(wú)論列表流暢度優(yōu)化也好、啟動(dòng)時(shí)間優(yōu)化也好還是說(shuō)其他方面的優(yōu)化,都有一些共性的原則,只有掌握了這些總體性的原則,才能夠更好的做優(yōu)化,給我們具體的優(yōu)化任務(wù)指明方向,讓我們少繞彎路。后面如果時(shí)間允許,我可能會(huì)寫一些關(guān)于列表流暢度、啟動(dòng)時(shí)間和內(nèi)存優(yōu)化等方面的文章。
我對(duì)優(yōu)化總體原則總結(jié)出包括不要提前過(guò)度優(yōu)化、要找到性能瓶頸、要在不同性能指標(biāo)間權(quán)衡、要理解優(yōu)化任務(wù)的底層運(yùn)行機(jī)制和要有技術(shù)保障體系五大原則,其中具體闡述每一個(gè)原則的時(shí)候并不局限于性能優(yōu)化方面,會(huì)發(fā)散到其他的相關(guān)領(lǐng)域,會(huì)對(duì)一些延伸的領(lǐng)域做一些簡(jiǎn)單的探討,希望能夠?qū)ψx者有一些啟示。以下是主要內(nèi)容。
一、不要提前過(guò)度優(yōu)化
這個(gè)原則包括優(yōu)化的過(guò)程中需要避免的兩個(gè)陷阱,即提前優(yōu)化和過(guò)度優(yōu)化。
提前優(yōu)化指的是在開(kāi)發(fā)的起始階段就把性能優(yōu)化作為一個(gè)重要的任務(wù)來(lái)考慮,在沒(méi)有實(shí)際數(shù)據(jù)指標(biāo)的基礎(chǔ)上,為了性能提前做的些盲目?jī)?yōu)化工作。
當(dāng)然這個(gè)觀點(diǎn)可能會(huì)引起爭(zhēng)議,因?yàn)樵谀承╅_(kāi)發(fā)領(lǐng)域,一些性能指標(biāo)以歷史的經(jīng)驗(yàn)來(lái)說(shuō),的確有很大概率甚至必然會(huì)有性能瓶頸的問(wèn)題,因此在架構(gòu)初期就需要考慮性能的問(wèn)題。 因此如果把“不要提前優(yōu)化“這個(gè)觀點(diǎn)推廣所有開(kāi)發(fā)領(lǐng)域上的話,我認(rèn)為可能不一定合適。但是如果把此觀點(diǎn)約束在iOS開(kāi)發(fā)這一領(lǐng)域內(nèi),我個(gè)人認(rèn)為還是成立的。因?yàn)樵谀壳半A段iOS平臺(tái)設(shè)備性能普遍較好,蘋果無(wú)論是硬件層面還是系統(tǒng)層面對(duì)性能方面都做了大量的優(yōu)化。所以我認(rèn)為性能方面并不是iOS開(kāi)發(fā)過(guò)程中需要首要考慮的因素。相比性能, 我個(gè)人認(rèn)為在iOS開(kāi)發(fā)的初始階段,以下幾個(gè)方面是更重要的,是需要首先考慮的。
首先需要考慮的是架構(gòu)的選擇,這里的架構(gòu)指的是Native架構(gòu)、web架構(gòu)、Native和web混合架構(gòu)和跨平臺(tái)的架構(gòu)。這里面我個(gè)人的意見(jiàn)是首先應(yīng)該盡量避免使用web架構(gòu),從Facebook早期的失敗經(jīng)驗(yàn)可以看出,web和Native相比的確存在諸多性能、體驗(yàn)等方面的問(wèn)題。連大廠都無(wú)法徹底改善webview的問(wèn)題,何況我們。但是在一些和用戶體驗(yàn)相比,對(duì)動(dòng)態(tài)化需求更加迫切的應(yīng)用場(chǎng)景下,是可以選擇web架構(gòu)的,比如大家都一直在吐槽某鐵路售票軟件。Native架構(gòu)的優(yōu)勢(shì)是產(chǎn)品體驗(yàn)好,對(duì)大多數(shù)iOS開(kāi)發(fā)者技術(shù)棧友好,缺點(diǎn)是由于蘋果對(duì)熱更新做了嚴(yán)格限制,導(dǎo)致一些動(dòng)態(tài)化的方案無(wú)法使用。Native和web混合架構(gòu)主要是在Native架構(gòu)上,在一些運(yùn)營(yíng)需求十分強(qiáng)烈的場(chǎng)景下(如電商等場(chǎng)景),某些模塊使用web開(kāi)發(fā),這樣既可以在App大部分場(chǎng)景下使用Native架構(gòu),保證用戶體驗(yàn),又滿足了部分場(chǎng)景動(dòng)態(tài)化的需求。跨平臺(tái)的架構(gòu)主要是可以減少多端開(kāi)發(fā)的成本,使用一套代碼完成iOS和Android兩個(gè)平臺(tái)的開(kāi)發(fā),目前主流的框架有ReactNative、Weex和Xamarin等。這些跨平臺(tái)架構(gòu)的愿景都很美好,但實(shí)際使用過(guò)程中,個(gè)人覺(jué)得現(xiàn)階段并不比使用Native架構(gòu)節(jié)省人力,其中會(huì)遇到許多已知的未知的坑,當(dāng)然作為新的技術(shù)我們應(yīng)該持開(kāi)發(fā)的心態(tài),但在使用時(shí)候也需要全面的評(píng)估,尤其作為一個(gè)可能有很長(zhǎng)生命周期的應(yīng)用,在使用非官方推薦的開(kāi)發(fā)架構(gòu)也好、開(kāi)源庫(kù)也好,如果后期無(wú)人維護(hù)的話,自己團(tuán)隊(duì)是不是有實(shí)力去接盤,如果不能,使用蘋果官方推薦的技術(shù)棧則更穩(wěn)妥些。
其次需要考慮的是開(kāi)發(fā)語(yǔ)言的選擇,這個(gè)方面其實(shí)在選擇了架構(gòu)之后,也將可選的開(kāi)發(fā)語(yǔ)言范圍縮小到幾個(gè)。而且實(shí)際項(xiàng)目開(kāi)發(fā)中并不難選,因?yàn)閳F(tuán)隊(duì)開(kāi)發(fā)人員的技術(shù)棧幾乎決定了使用的開(kāi)發(fā)語(yǔ)言,如果使用了大部分團(tuán)隊(duì)成員都不熟悉的語(yǔ)言,我相信即使所選語(yǔ)言在很多方面都有壓倒性的優(yōu)勢(shì),項(xiàng)目的推進(jìn)也不會(huì)十分順利。但是排除團(tuán)隊(duì)技術(shù)棧的因素,不同的開(kāi)發(fā)語(yǔ)言的確是各有千秋。大家都知道iOS開(kāi)發(fā)過(guò)程中,如果使用Native架構(gòu),官方的開(kāi)發(fā)語(yǔ)言是objc和swift。objc是早期iOS開(kāi)發(fā)的官方推薦語(yǔ)言,優(yōu)點(diǎn)是其動(dòng)態(tài)性,十分靈活,可以實(shí)現(xiàn)許多“黑魔法”,缺點(diǎn)語(yǔ)法略怪異(當(dāng)然對(duì)于iOS開(kāi)發(fā)者,使用久了也不覺(jué)得怪了),另外是對(duì)一些高級(jí)的語(yǔ)言特性支持的不是很好(記得最初使用objc開(kāi)發(fā)iOS應(yīng)用的時(shí)候,因?yàn)橐恍┨厥獾男枨蟆S捎趏bjc不支持namespace,給團(tuán)隊(duì)造成了很大的困擾)。swift是蘋果近年來(lái)主推的開(kāi)發(fā)語(yǔ)言, 其吸收了許多其他語(yǔ)言的先進(jìn)特性,也比較容易上手。關(guān)于兩種開(kāi)發(fā)語(yǔ)言的具體技術(shù)細(xì)節(jié),大家有興趣可以自己查看一些資料了解下。雖然蘋果一直在力推swift,但是目前在國(guó)內(nèi)iOS開(kāi)發(fā)領(lǐng)域,由于一些用戶基數(shù)大的主流App,均是在swift出現(xiàn)前使用objc編寫的,而且大多經(jīng)過(guò)了數(shù)年的版本迭代,加之早起swift ABI的不穩(wěn)定和版本之間升級(jí)需要較多工作,還有swift和objc混編的一些問(wèn)題,導(dǎo)致目前國(guó)內(nèi)主流App大多仍使用objc作為開(kāi)發(fā)語(yǔ)言。在一些創(chuàng)業(yè)公司,或者新的項(xiàng)目中,才有部分開(kāi)發(fā)者使用swift,當(dāng)然如果目光放長(zhǎng)遠(yuǎn)的話,未來(lái)一定是swift的天下,這兩年objc在每年的語(yǔ)言排名中逐年下降也側(cè)面印證了這一點(diǎn)。 除了官方推薦的objc和是swift之外,如果使用跨平臺(tái)等其他架構(gòu),還可以使用如js、c#等語(yǔ)言,有興趣的可以自行了解下。
再次需要考慮的是開(kāi)發(fā)過(guò)程中具體的代碼架構(gòu)的選擇,這里只簡(jiǎn)單談?wù)凬ative架構(gòu)下的代碼架構(gòu)選擇。 目前iOS開(kāi)發(fā)中常用的架構(gòu)有MVC 、MVVM、VIPER、MVP等。關(guān)于這些架構(gòu),網(wǎng)上目前有很多的介紹,大家如果對(duì)具體細(xì)節(jié)有興趣可以自行查閱。這里我只想補(bǔ)充一點(diǎn),大家在學(xué)習(xí)和實(shí)踐時(shí),不要盲目跟風(fēng)新技術(shù),比如MVVM等架構(gòu)未必比MVC好很多,MVC也未必是一個(gè)過(guò)時(shí)的框架。要知道很多新架構(gòu)帶來(lái)的擴(kuò)展性和解耦行都是通過(guò)引入間接層來(lái)實(shí)現(xiàn)的,隨之而來(lái)的可能是更多的膠水代碼和更復(fù)雜的代碼結(jié)構(gòu)。希望大家在選擇的時(shí)候能夠根據(jù)項(xiàng)目的特點(diǎn)和團(tuán)隊(duì)自身的狀況,選擇最適合自己團(tuán)隊(duì)和項(xiàng)目的代碼架構(gòu)。
除了上面說(shuō)的三點(diǎn),還有一些其他的關(guān)鍵點(diǎn)需要大家在項(xiàng)目初期考慮,比如如何在團(tuán)隊(duì)內(nèi)部達(dá)成統(tǒng)一的代碼風(fēng)格?一些關(guān)鍵的技術(shù)如何選型?如何保證代碼結(jié)構(gòu)清晰、簡(jiǎn)單、擴(kuò)展性好等等。性能問(wèn)題可以在項(xiàng)目后期開(kāi)始考慮,如果真的發(fā)現(xiàn)明顯的性能問(wèn)題再優(yōu)化也來(lái)得及。比如項(xiàng)目一開(kāi)始憑直覺(jué)感覺(jué)某一個(gè)模塊可能會(huì)有性能問(wèn)題,就盲目使用多線程,而不是根據(jù)實(shí)際情況具體問(wèn)題具體分析。會(huì)導(dǎo)致程序復(fù)雜且容易出現(xiàn)線程安全問(wèn)題。
過(guò)度優(yōu)化是指為了優(yōu)化性能,過(guò)度增加系統(tǒng)復(fù)雜度和維護(hù)成本,使得開(kāi)發(fā)周期變長(zhǎng)。雖然可能性能上帶來(lái)了一定的提升,但是和過(guò)度優(yōu)化而導(dǎo)致的這些缺點(diǎn)來(lái)比,這么做顯而易見(jiàn)是得不償失的。
筆者在工作過(guò)程中,發(fā)現(xiàn)許多同學(xué)在性能優(yōu)化過(guò)程中,都容易陷入這種過(guò)度優(yōu)化的陷阱。比如這一個(gè)簡(jiǎn)單的設(shè)置界面,一共只有十幾個(gè)靜態(tài)的cell, 如果去考慮圓角性能、離屏渲染、圖片緩存、高度緩存、異步渲染甚至緩存布局信息,這些無(wú)疑是陷入了過(guò)度優(yōu)化的陷阱,在這個(gè)應(yīng)用背景下,簡(jiǎn)單快速的實(shí)現(xiàn)功能才是第一要?jiǎng)?wù)。在目前蘋果的開(kāi)發(fā)框架和平臺(tái)上,一般如果出現(xiàn)性能問(wèn)題,以我的實(shí)際經(jīng)驗(yàn)來(lái)說(shuō),問(wèn)題大部分是出在業(yè)務(wù)邏輯上面,所以遇到問(wèn)題首先需要在業(yè)務(wù)邏輯上找問(wèn)題,一些過(guò)度的極限的優(yōu)化,完全是沒(méi)有必要的。
其實(shí)不光是性能優(yōu)化,我發(fā)現(xiàn)許多同學(xué)在日常開(kāi)發(fā)中,處處都有過(guò)度設(shè)計(jì)的情況。比如設(shè)計(jì)模式中的design happy這一陷阱,許多初學(xué)者在剛開(kāi)始學(xué)習(xí)設(shè)計(jì)模式的時(shí)候,十分癡迷設(shè)計(jì)模式在解決不同問(wèn)題時(shí),對(duì)代碼的解耦性和可擴(kuò)展性上的威力,在開(kāi)發(fā)過(guò)程中會(huì)時(shí)時(shí)刻刻想著應(yīng)該用什么設(shè)計(jì)模式。結(jié)果導(dǎo)致很多的過(guò)度設(shè)計(jì),其實(shí)我們寫代碼過(guò)程中,如果能遵守基本的SOLID原則,大部分情況下就可以寫出高質(zhì)量的代碼。
另外一個(gè)例子是組件化。近期iOS組件化是一個(gè)十分流行的話題,有許多團(tuán)隊(duì)提出了不同的組件化方案。實(shí)際項(xiàng)目中,團(tuán)隊(duì)在是否采用組件化方式開(kāi)發(fā)的選擇上,我希望要結(jié)合項(xiàng)目特點(diǎn)和團(tuán)隊(duì)組織架構(gòu)形式具體問(wèn)題具體分析,不要盲目跟風(fēng)。在產(chǎn)品功能相對(duì)單一、開(kāi)發(fā)人員較少、并行開(kāi)發(fā)需求不強(qiáng)烈的情況下,推行組件化,不但增加系統(tǒng)復(fù)雜度,而且增加開(kāi)發(fā)人員學(xué)習(xí)成本高,使得開(kāi)發(fā)成本變大,我個(gè)人覺(jué)得這種規(guī)模的應(yīng)用初期需要更多考慮的是如何快速上線、快速迭代和保證App質(zhì)量。因此如果能夠進(jìn)行清晰的分層,嚴(yán)格遵守簡(jiǎn)單統(tǒng)一的架構(gòu)模式即可。組件化比較適合從功能形態(tài)上可以清晰劃分若干模塊的產(chǎn)品,比如美團(tuán)、58同城、淘寶和攜程等產(chǎn)品,內(nèi)部有多個(gè)業(yè)務(wù)模塊,而且這些公司開(kāi)發(fā)此類“航母”App的時(shí)候,會(huì)從組織架構(gòu)把不同業(yè)務(wù)劃分給不同的開(kāi)發(fā)團(tuán)隊(duì),為了能夠保證不同團(tuán)隊(duì)之間能夠獨(dú)立并行開(kāi)發(fā)和發(fā)版,最大程度上減少代碼的依賴程度,這個(gè)時(shí)候應(yīng)用組件化則是最佳實(shí)踐。
上面是對(duì)不要提前過(guò)度優(yōu)化原則的詳細(xì)闡述,并引申到相關(guān)開(kāi)發(fā)領(lǐng)域,做了一些不成熟的探討。下面介紹性能優(yōu)化總體原則的第二個(gè)。
二、要找到性能瓶頸
在做優(yōu)化前,一定要首先找到性能瓶頸有哪些,依性能嚴(yán)重程度逐個(gè)解決。不要盲目?jī)?yōu)化,否則最后可能花了很大的力氣,優(yōu)化掉的可能只是性能損耗很小的一部分。這一原則我覺(jué)得尤為重要,因?yàn)槲以诠ぷ髦杏鲆?jiàn)過(guò)包括我在內(nèi),很多不進(jìn)行性能瓶頸查找,全憑主觀猜測(cè)進(jìn)行性能優(yōu)化的情況。 在尋找性能瓶頸過(guò)程中,也需要注意以下問(wèn)題。
不要主觀猜測(cè),讓性能評(píng)測(cè)數(shù)據(jù)說(shuō)話。
這一點(diǎn)十分重要,要時(shí)刻記住要以事實(shí)說(shuō)話,不要以為某個(gè)函數(shù)使用的算法的時(shí)間復(fù)雜度是O(n2)就覺(jué)得一定會(huì)有性能問(wèn)題,非要費(fèi)很大力氣優(yōu)化到O(n*logn), 殊不知你的輸入數(shù)據(jù)可能只有幾十或者幾百個(gè),即使O(n2)也不會(huì)有多大的性能問(wèn)題。也不要以為某個(gè)方法僅僅調(diào)用了系統(tǒng)庫(kù)的一個(gè)簡(jiǎn)單get方法,就不會(huì)有什么性能問(wèn)題,殊不知這個(gè)get方法里可能包含一些十分耗時(shí)的操作(比如磁盤IO)。因此在遇到性能問(wèn)題的時(shí)候,一定不要憑主觀猜測(cè),實(shí)地跑一下性能數(shù)據(jù),讓數(shù)據(jù)告訴我們性能瓶頸究竟在哪里。
要使用恰當(dāng)?shù)男阅茉u(píng)測(cè)工具。
對(duì)于開(kāi)發(fā)版本的性能優(yōu)化,Xcode提供的instruments絕對(duì)是最好的尋找性能瓶頸的工具,沒(méi)有之一。instruments有豐富的性能評(píng)測(cè)工具,包括常用的Core Animation、Time Profiler、Leaks和Allocations等等。這些工具在分析函數(shù)執(zhí)行時(shí)間、fps和內(nèi)存等方面給我提供了十分便捷的功能。在使用instruments過(guò)程中需要注意:
要使用真機(jī),而不是模擬器。模擬器的CPU比iOS機(jī)器要快很多,所以在模擬器上,CPU相關(guān)的操作會(huì)更快。因?yàn)镸ac的GPU和iOS設(shè)備上的GPU不同,所以模擬器需要在CPU上通過(guò)軟件去模擬iOS設(shè)備上的GPU,所以GPU相關(guān)的操作會(huì)更慢。因此如果使用模擬器去進(jìn)行性能優(yōu)化的話,評(píng)測(cè)設(shè)備和真實(shí)用戶設(shè)備性能表現(xiàn)的不一致,會(huì)導(dǎo)致優(yōu)化的效果大打折扣。這里面內(nèi)存是一個(gè)例外,在做內(nèi)存優(yōu)化的時(shí)候,使用模擬器和真機(jī)一般差別不大,可以使用模擬器進(jìn)行內(nèi)存的優(yōu)化。
要使用Release配置而不是Debug配置,因?yàn)樵趓elease包的時(shí)候,編譯器會(huì)做一些優(yōu)化以提高性能。自己的工程代碼可能也會(huì)在release下做一些優(yōu)化,比如去除log信息和一些debug功能等。我們關(guān)心的是release下的性能,因?yàn)橛脩糇罱K也是使用的release的安裝包。所以測(cè)試的時(shí)候要一定要記住在release配置下進(jìn)行,instruments進(jìn)行性能評(píng)測(cè)的時(shí)候,默認(rèn)是在release下進(jìn)行的。但是工程代碼里面的優(yōu)化則需要自己注意。筆者就曾經(jīng)為此付出過(guò)很大代價(jià),因?yàn)闆](méi)有注意工程代碼里面的一些debug功能,導(dǎo)致優(yōu)化過(guò)程中錯(cuò)誤的認(rèn)為動(dòng)態(tài)庫(kù)是影響啟動(dòng)時(shí)間的罪魁禍?zhǔn)祝撕艽罅獍褎?dòng)態(tài)庫(kù)修改為靜態(tài)庫(kù),白白浪費(fèi)了很多時(shí)間。
要使用性能相對(duì)差的機(jī)器進(jìn)行評(píng)測(cè),因?yàn)槲覀冃枰WC的是我們的應(yīng)用在性能差的機(jī)器上也有良好的表現(xiàn)。如果有條件,最好能夠覆蓋多個(gè)機(jī)型,和我們傳統(tǒng)上的認(rèn)識(shí)不同,機(jī)型越新性能不一定越高,例如iPad3在動(dòng)畫和渲染性能上比iPad2差。
要覆蓋不同系統(tǒng)版本,因?yàn)樵趇OS系統(tǒng)上,一般系統(tǒng)版本越高,同一機(jī)器性能大體上趨于差,所以如果只覆蓋低版本的機(jī)型,可能在高版本上表現(xiàn)的性能會(huì)不盡如人意。
除了使用instruments,還可以使用log等方式進(jìn)行查找性能瓶頸。
對(duì)于線上的App,查找性能瓶頸的工具主要是Application Performance Management(APM)。目前各大公司基本都有自己的APM,主要是對(duì)線上App進(jìn)行性能監(jiān)控以及預(yù)警。因?yàn)樵陂_(kāi)發(fā)和測(cè)試階段,由于使用人數(shù)相對(duì)比較有限,很難覆蓋所有的業(yè)務(wù)場(chǎng)景。而在App發(fā)布出去后,大量用戶在使用過(guò)程中的性能表現(xiàn),會(huì)給我們的App帶來(lái)更全面的性能評(píng)測(cè)數(shù)據(jù),因此線上App的性能監(jiān)控是十分重要的。
要抓重點(diǎn),有的放矢。找到性能損耗大的前N個(gè)問(wèn)題,依重要程度和解決的難易程度解決,這樣才能花最少的精力,解決最大問(wèn)題。
三、要在不同性能指標(biāo)間權(quán)衡,達(dá)到總體最優(yōu)
在已經(jīng)找到性能瓶頸的時(shí)候,解決性能問(wèn)題的方法則需要具體問(wèn)題具體分析,要在不同性能指標(biāo)間權(quán)衡,以達(dá)到總體最優(yōu)。
這需要我們要有整體的意識(shí),App的性能可以分為很多類,不同的性能指標(biāo)對(duì)用戶體驗(yàn)造成的影響也不盡相同,比如fps主要影響的是用戶的滑動(dòng)體驗(yàn),頁(yè)面加載時(shí)間和應(yīng)用啟動(dòng)時(shí)間影響的是用戶等待時(shí)間上的體驗(yàn)。我們?cè)趦?yōu)化的過(guò)程中,要牢記我們的目標(biāo)是希望App的整體體驗(yàn)最優(yōu),而不是某一單項(xiàng)的性能指標(biāo)最優(yōu)。不同優(yōu)化指標(biāo)之間可能是呈正相關(guān),比如優(yōu)化了滑動(dòng)過(guò)程中大量函數(shù)的耗時(shí)時(shí)間,使得fps性能提升,可能會(huì)導(dǎo)致App的耗電量變少。同時(shí),不同優(yōu)化指標(biāo)也可能是負(fù)相關(guān)、相互制約的,比如為了流暢性做了過(guò)多的cache,會(huì)導(dǎo)致內(nèi)存性能下降,甚至導(dǎo)致因?yàn)閙emory warning導(dǎo)致被系統(tǒng)kill掉,這無(wú)疑對(duì)App的整體體驗(yàn)造成了負(fù)面的影響。因此實(shí)際優(yōu)化過(guò)程中需要我們反復(fù)權(quán)衡利弊和取舍,達(dá)到整體的性能最優(yōu)
四、要理解優(yōu)化任務(wù)的底層運(yùn)行機(jī)制
如果不理解優(yōu)化任務(wù)的底層運(yùn)行機(jī)制,可能很難達(dá)到更好的優(yōu)化效果。
比如在做啟動(dòng)時(shí)間優(yōu)化的時(shí)候,如果你不知道iOS中App的啟動(dòng)時(shí)間是由main之前和main之后兩部分時(shí)間組成的,此時(shí)如果你的App是因?yàn)閙ain函數(shù)之前的部分占用了過(guò)多的啟動(dòng)時(shí)間,可能你花了大量的精力去優(yōu)化main之后的時(shí)間卻沒(méi)有達(dá)到好的優(yōu)化效果。如果你不知道App啟動(dòng)過(guò)程的運(yùn)行機(jī)制,就無(wú)法知道去檢查是否鏈接了過(guò)多的自定義的動(dòng)態(tài)庫(kù)或者去load函數(shù)里面確認(rèn)是否有耗時(shí)的操作等等。還有在做fps優(yōu)化的時(shí)候,如果不了解卡頓的底層原因是什么、一個(gè)view從創(chuàng)建到顯示過(guò)程中經(jīng)歷那些步驟、CPU和GPU在這個(gè)過(guò)程中都扮演什么角色,則很難做到絲滑般的順暢?還有在做內(nèi)存優(yōu)化的時(shí)候,如果不了解內(nèi)存分為哪幾類、系統(tǒng)對(duì)App和不同類型extension的內(nèi)存限制機(jī)制的不同、超過(guò)限制系統(tǒng)會(huì)采取什么操作等等,也很難把內(nèi)存優(yōu)化做好。因此只有深入了解底層機(jī)制才能更好的有針對(duì)性的提出更優(yōu)的解決方案。
其實(shí)不只是在性能優(yōu)化方面,在開(kāi)發(fā)過(guò)程中很多情況下,了解底層的原理會(huì)讓你變得更高效,更容易解決遇到的各種問(wèn)題。在這里分享一個(gè)我印象比較深的一次經(jīng)歷。記得有一次在開(kāi)發(fā)某個(gè)功能的時(shí)候,需要用到level db數(shù)據(jù)庫(kù),在開(kāi)發(fā)過(guò)程中做單元測(cè)試的時(shí)候發(fā)現(xiàn),level db的本地存儲(chǔ)文件在不斷刪除和寫入的過(guò)程中,越變?cè)酱螅踔吝_(dá)到1G大小。當(dāng)時(shí)第一印象以為是在使用上出了問(wèn)題,所以上層業(yè)務(wù)邏輯上查找問(wèn)題,結(jié)果查了很久都沒(méi)有找到問(wèn)題。但如果我在使用level db的時(shí)候去多了解一下其底層的實(shí)現(xiàn)原理,了解LSM(Log-Structured-Merge Tree)的原理,遇到這個(gè)問(wèn)題的時(shí)候就不會(huì)認(rèn)為這是一個(gè)bug,也不會(huì)浪費(fèi)了大把的時(shí)間來(lái)做無(wú)用功。所以建議大家不要抱怨每天的工作過(guò)于簡(jiǎn)單枯燥,在開(kāi)發(fā)過(guò)程中多去挖掘一些深層次的東西,不但讓你的技術(shù)的深度不斷加深,也會(huì)對(duì)你的編碼效率有很大的提升。
五、要有技術(shù)保障體系
性能優(yōu)化不能一勞永逸,我個(gè)人覺(jué)得更是一場(chǎng)持久戰(zhàn)。不僅需要你能夠在某個(gè)特定時(shí)期做專項(xiàng)優(yōu)化的攻堅(jiān)戰(zhàn),還要為打好持久戰(zhàn)做出好的后勤補(bǔ)給,為了能使App長(zhǎng)期保持好的性能,不僅僅需要開(kāi)發(fā)人員有良好的開(kāi)發(fā)技能,還需要有一些技術(shù)保障和體系。下面簡(jiǎn)單羅列我能想到的幾個(gè)方面。
要有好的測(cè)試保障,這里的測(cè)試保障不僅僅指的是測(cè)試人員的手動(dòng)測(cè)試,更需要的自動(dòng)化測(cè)試。要建立針對(duì)不同性能指標(biāo)的專項(xiàng)自動(dòng)化測(cè)試,建立一套從定時(shí)運(yùn)行測(cè)試到測(cè)試結(jié)果的輸出等一套完整的自動(dòng)化測(cè)試體系,能夠?yàn)樾阅艿谋WC提供堅(jiān)實(shí)的數(shù)據(jù)支撐。
引入關(guān)鍵性能指標(biāo)上線準(zhǔn)入制度。在開(kāi)發(fā)階段,為了不將有性能問(wèn)題的代碼帶到線上,可以將比如啟動(dòng)時(shí)間、FPS、安裝包大小等指標(biāo)作為關(guān)鍵指標(biāo),上線前進(jìn)行自動(dòng)化測(cè)試,如指標(biāo)不達(dá)標(biāo),不允許上線。
對(duì)于線上App使用APM進(jìn)行監(jiān)控,發(fā)現(xiàn)線上的性能問(wèn)題,有及時(shí)的預(yù)警機(jī)制,能夠隨時(shí)解決線上的性能問(wèn)題。
開(kāi)發(fā)過(guò)程中,代碼中需要有對(duì)性能保障的設(shè)計(jì)。 比如可以設(shè)計(jì)可復(fù)用的高性能控件,這樣其他開(kāi)發(fā)人員在開(kāi)發(fā)類似功能時(shí),可以簡(jiǎn)單復(fù)用,不僅提升了性能,而且大大節(jié)省了開(kāi)發(fā)的時(shí)間。還有比如為了防止App隨著版本迭代導(dǎo)致啟動(dòng)時(shí)加載的服務(wù)越來(lái)約多,導(dǎo)致啟動(dòng)變慢,可以設(shè)計(jì)App啟動(dòng)器,把這些任務(wù)統(tǒng)一放到主界面加載完成后再執(zhí)行,并且在組內(nèi)開(kāi)發(fā)人員中形成硬性的規(guī)范,但凡啟動(dòng)時(shí)期不必須的服務(wù),要么不要執(zhí)行,要么統(tǒng)一放到啟動(dòng)器的主界面加載完成的回調(diào)中執(zhí)行。
定期做一些性能優(yōu)化方面的技術(shù)分享,不僅僅可以提高組內(nèi)同學(xué)的開(kāi)發(fā)技能,還可以活躍組內(nèi)的技術(shù)氣氛。
以上我對(duì)iOS性能優(yōu)化的總體原則做的總結(jié),希望能夠?qū)Υ蠹矣幸稽c(diǎn)點(diǎn)啟示。其中可能很多想法并不成熟,也希望大家能夠多多批評(píng)互相探討,共同進(jìn)步。