筆者因爲在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性能優化的整體原則作的總結,但願可以對你們有一點點啓示。其中可能不少想法並不成熟,也但願你們可以多多批評互相探討,共同進步。