開發完 iOS 應用,接下去你該作的事

iOS專項總結git

 

一個應用通過屢次迭代後告一段落,接下去咱們在技術上還能夠作些什麼呢?答案是提升代碼的總體質量。關於這方面,除了咱們常喊的 重構,測試也很是重要。程序員

 

博主近期給咱們的 iOS客戶端代碼來了一次專項測試。主要從常規的 輔助測試 入手,來了次代碼大清理,找到代碼中的問題,並一一改掉它們。驚喜的是,這對於提升本人的代碼水平有很大的幫助。其實,這套代碼的質量自己已經很高了,也很是整潔。而這主要得益於嚴格的代碼規範和pull request機制。github

 

關於測試,App常關注的每每是一些功能性的,包括單元測試,用monkey在界面上點擊看頁面表現是否正確等等。 我之前還搭過一個 aspectJ + robotium。(這是Java上的) 然而,測試更應該覆蓋代碼質量,性能檢測等等。算法

下面給出一副我理解測試的結構圖:服務器

 

順着這幅圖,咱們能夠從靜態測試入手。網絡

 

關於 analyzeapp

 

在Xcode 中執行product -> analyze工具,出現了粗粗細細的箭頭標識表示調用邏輯。async

 

這個工具所找出的主要是代碼中的邏輯問題,自己能發現的問題不少。實際運用到工程代碼上,發現的問題主要分爲如下四點:工具

 

  • 好比初始化賦值未被使用過。oop

  • 好比未調用super語句。

  • 好比從未使用過的變量。

  • 好比邏輯上錯誤的代碼。(這點極少)

 

可是關於資源文件,該工具是掃描不出來的。另外,咱們能夠在Xcode中配置該工具。

 

Clang 靜態分析器

 

analyze 的底層其實就是 Clang 靜態分析器,包含 ’shallow’ 和 ‘deep’ 深淺兩種模式。

 

深模式比淺模式慢不少,由於多出了跨方法的 控制流分析 以及 數據流分析。(如前文那副圖)

 

建議:
開啓分析器的 所有檢查(方法是在 build setting 的 「Static Analyzer」 部分開啓全部選項)
在 build setting 裏,對 release 的 build 配置開啓 「Analyzer during ‘Build’「。(真的,必定要這樣作 — 你不會記得手動跑分析器的。)
把 build setting 裏的 「Model of Analysis for ‘Analyze’「設爲 Shallow(faster)
把 build setting 裏的 「Model of Analysis for ‘Build’「設爲 Deep

 

但這兩種對於公司這麼一個大項目來講都不快,假如在 build setting裏 開啓 Model of Analysis for ‘Build’ 並設爲淺模式,那麼每次運行就會總體跑一遍靜態分析並不合理。

 

還有一種設想是在打 ad hoc 包前自動跑一遍 靜態分析,是比較合理的也能夠在XCode中開啓。可是咱們編譯打包用的是自動化工具,服務器直接從github讀取而不是經過XCode,會跳過這個分析。

比較合理的作法是按期 跑一遍 analyzer 工具。(好比每週五)而不是 build , release 之前作這事。(問了下其餘同事的意見)

 

目前靜態分析在工程中有 這麼幾塊不改的問題: 首先是網上拷過來的算法。包括加密算法,模糊算法,64位編譯算法等等。裏面有一些遠大於,遠小於,與運算等。看不懂的地方不改。

 

爲何不改呢?由於沒看出邏輯問題。(或者看懂了也不敢改,怕算法錯誤。)

 

Slender

 

slender 是一款針對 iOS 圖片分析的 Mac 軟件。導入工程,能夠找到全部未使用到的 resource。(unused 這軟件也能夠找到多餘的資源)

 

除此之外,它還給出了全部資源文件的建議。包括你缺省的 x3, x2圖片版本等。

 

Faux Pas

 

意外發現的驚喜。Faux Pas 是一個出色的靜態 Error 檢測工具。

 

slender 找到的是未使用的資源,faux 找到的是代碼中使用到,但不存在的image。(實測下來每每有不少)

 

甚至由此 發現了一些棄用的文件夾。

 

不只如此,它有一百多個error 和 warning 規則。包括 未定義的 user define runtime attributes(跑在runtime上)。這一點平時靠程序員的肉眼狠難見。曾經你爲了圖省事直接在 xib 中炫技,加上了一些 key。然而當代碼變遷,刪掉一個類的時候,根本想不到 到 xib中去刪除 對應的部分。

 

還有一些代碼規範上的問題。

 

好比界面上的文字未本地化;在release 版本依舊有NSLog 輸出;NSPhotoLibraryUsageDescription在info.plist中未定義;被定義成strong類型的delegate等等。

 

和一些配置上的建議。

 

Warning

 

你的pods 中還在報 warning 嗎?你可使用下面這種方法屏蔽它們。

 

在podfile 的開頭加上這樣一句話:

 

inhibit_all_warnings!

 

就能屏蔽 pods 中全部代碼上的 warning了。

 

而後惋惜的是,對於編譯上的 warning卻一籌莫展。注:iOS中不能阻止 Xcode 報warning 的選項。只能屏蔽某一文件,或某工程的warning。好比咱們已屏蔽的pods中 代碼的錯誤。可是屏蔽不了系統在編譯過程當中 非代碼級別的 警告。

 

而且也不建議屏蔽這種警告,好比因爲友盟未支持 7.0 版本所致使的 警告。如今因爲 友盟自己未支持,7.0 用戶還有許多等等緣由,但將來或許能解決。不該忘記這點。

 

Leaks

 

蘋果集成了一個性能檢測工具,叫instruments。使用instrument中的Leaks工具:

 

iOS內存泄露點比較少,大部分都是系統內部方法,或者一些庫裏的。咱們對此別無辦法。實測下來,主要的泄露場景集中在 UIKeyboard 上。在我嘗試了各類點擊場景後,系統彈框時候最容易泄露。

 

假如你的代碼中真有泄露,那它們可能集中在快速滑動 Listing的時候,加載大批量圖片的時候。在咱們的應用中,這些大都使用了網上開源的成熟庫。

 

對於 iOS可能泄露的地方,個人建議仍是經過已知的代碼規範 逆向去尋找。好比 誤用 strong類型的delegate; block 中沒有使用 weak self等等。

 

Time Profiler

 

時間都去哪兒了。這個工具能夠找出咱們最耗時的操做並定位到代碼中。

 

實際使用過程當中,確實發現了一處不合理的耗時循環。

 

(右側黑色部分是耗時操做,但不必定是錯誤的代碼)

 

工具只是統計時間的消耗,更重要的是經過對代碼的敏感,分析定位出可優化的代碼。舉個例子,有這麼一段代碼。

 

有個循環在判斷時候使用了枚舉類型,但因爲枚舉類型獲取不到count,因此直接寫死了循環10次。

 

但其實咱們能夠在枚舉類型的末尾加上TYPE_MAX。這樣既擁有了枚舉在switch時候的優點,又獲得了NSArray獲取count的辦法。

 

加載時間

 

加載時間上,主要看看不一樣網絡下出首頁有沒有卡死等。公司wifi條件真機上測試大約 1.2s 內加載完畢,而當模擬邊緣網絡時主線程加載依然迅速,沒發現什麼問題。

 

關於啓動時間,有兩種查看方法。一種是經過上文的時間檢測工具,另外一種是直接在代碼中打log。其中log又有兩種打法。

 

分別說下這兩種打法:

 

CFAbsoluteTime StartTime;

int main(int argc, char **argv) {

     StartTime = CFAbsoluteTimeGetCurrent();

     // ...

}

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {

     dispatch_async(dispatch_get_main_queue(), ^{

        NSLog(@"Launched in %f sec", CFAbsoluteTimeGetCurrent() - StartTime);

     });

     // ...

}

 

這是一般的打印方法。然而,從main 開始啓動有太多的不可控因素。

 

iOS App啓動過程

 

  • 連接並加載Framework和static lib

  • UIKit初始化

  • 應用程序callback

  • 第一個Core Animation transaction

 

其中 framework的加載過程咱們難以控制,而初始化字體、狀態欄、user defaults、main nib等的過程,咱們也不須要關心。固然,你能夠作的是保持它們儘量小,沒有冗餘。

 

因此,還有一種作法是記錄你在 didFinishLaunchingWithOptions 中所耗費的時間。在這裏,你可能初始化了許多沒必要須的第三方庫,作了幾回網絡請求。而這些,咱們均可以 lazyLoad,讓程序儘快地冷啓動。

 

幀率等

 

如何優雅地顯示幀率標籤?

 

在 QuartzCore.framework裏,有一個 CADisplayLink 類。系統在渲染每一幀的時候,都會調用一次CADisplayLink中的 selector, 它有點像一個定時器類,默認爲一秒調用60次。

 

幀率大部分時間都穩定在 60fps,然而有兩種狀況下會致使它不能以每秒60次的頻率調用回調方法。

 

  • CPU忙於其餘計算,無暇執行屏幕繪製動做。

  • 執行回調方法所用時間大於重繪每幀的間隔時間。

 

咱們能夠利用這個類的特性,寫出以下代碼來實時顯示一個 屏幕幀率 的標籤。

 

CADisplayLink displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(tick:)];  

[_displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSRunLoopCommonModes];

 

- (void)tick:(CADisplayLink *)displayLink {

//...

self.numberOfFrames++;

NSTimeInterval delta = displayLink.timestamp - self.lastTimestamp;

    

    // Less than one second

    if (delta < 1) {

        return;

    }

    

    CGFloat fps = self.numberOfFrames / delta;

    

    //Update label

    ....

}

 

這個fps 的數值,即是每秒幀數。實測當下拉刷新,或加載List過快時,都會往下掉得很厲害。而這即是性能瓶頸所在的關鍵點。

相關文章
相關標籤/搜索