如何有效提高swift的編譯速度

swift 作為一門新語言,受到了廣大開發(fā)者的喜愛,蘋果也極力在推swift,甚至最終會(huì)替換掉OC,筆者現(xiàn)在公司的項(xiàng)目也采用了swift編寫(三方庫除外),但發(fā)現(xiàn)一個(gè)很嚴(yán)重的問題就是編譯速度實(shí)在是難以讓人接受,因此筆者在如何提高swift的編譯速度方面做了相關(guān)調(diào)研,并將所學(xué)到的應(yīng)用到項(xiàng)目中,項(xiàng)目的編譯速度得到了很大的提升。文章將從兩方面來介紹如何提高swift項(xiàng)目的編譯速度,一是從代碼優(yōu)化上,一是從編譯器設(shè)置上。

前言#

在改善項(xiàng)目的編譯速度前,有必要知道到底是哪些函數(shù)編譯耗時(shí),哪些文件編譯耗時(shí),Robert 一個(gè)swift愛好者為我們提供了一個(gè)統(tǒng)計(jì)函數(shù)編譯時(shí)間的工具https://github.com/RobertGummesson/BuildTimeAnalyzer-for-Xcode,利用該工具能很方便的查出編譯耗時(shí)的地方。

一代碼層面優(yōu)化#

1.盡量避免類型推斷,能確定類型的一定要給出具體類型

func test1() {
        let number = 32
        let string = ""
        let label = UILabel()
        let dict = ["string1":"string","number":10,"label":label] as [String : Any]
        var strings: [String] = []
    }
    func test2() {
        let number:Int = 32
        let string:String = ""
        let label:UILabel = UILabel()
        let dict:[String:Any] = ["string1":"string","number":10,"label":label] as [String : Any]
        var strings: [String] = [String]()
    }
屏幕快照 2017-05-01 22.36.45.png

test1采用了類型推斷耗時(shí)37.3ms,test2采用了精確的類型定義耗時(shí)10.5ms,減少了近三倍多的編譯時(shí)間。

2.nil類型問題
由于swift存在可選值,因此某些對(duì)象的值可能為空,這在代碼處理時(shí)可能會(huì)導(dǎo)致編譯很慢

func test3() ->Int {
        var number1:Int?
        var number2:Int?
        var number3:Int?
        return 10 + (number1 ?? 0) + (number2 ?? 0) + (number3 ?? 0)
    }
    func test4() ->Int {
        var total = 10
        var number1:Int?
        var number2:Int?
        var number3:Int?
        if let number1 = number1 {
            total = total + number1
        }
        
        if let number2 = number2 {
            total = total + number2
        }
        if let number3 = number3 {
            total = total + number3
        }
        return total
    }
屏幕快照 2017-05-01 22.44.38.png

test3中number1,2,3可能存在nil,因此在返回時(shí)如果為nil,則給了默認(rèn)值0,結(jié)果編譯時(shí)間為7841.3ms,將近8s,太不可思議。而test4中對(duì)于可能為nil的情況下進(jìn)行了可選值綁定來判斷是否為nil,編譯時(shí)間為1.7ms,編譯時(shí)間跟test3不在一個(gè)量級(jí)上面,因此對(duì)于可能為nil的情況下,建議采用可選值綁定的方式來判斷,避免采用三的處理方式。

3.+ +=運(yùn)算
直接看代碼

func test5() {
        var arrays = [Int]()
        let arr1 = [1,2,3]
        let arr2 = [3,4,5]
        arrays += arr1 + arr2 + [10]
    }
    func test6() {
        var arrays:[Int] = [Int]()
        let arr1 = [1,2,3]
        let arr2 = [3,4,5]
        arrays.append(contentsOf: arr1)
        arrays.append(contentsOf: arr2)
        arrays.append(contentsOf: [10])
    }
屏幕快照 2017-05-01 22.54.22.png

test5采用+ 將數(shù)組進(jìn)行合并耗時(shí)140.9ms,而test5采用系統(tǒng)提供的api進(jìn)行合并耗時(shí)2.3ms,因此對(duì)于數(shù)組合并的情況建議采用test6的形式。

4.復(fù)雜表達(dá)式計(jì)算
直接看代碼

func test7(string1:String,string2:String) {
        let string = string1 + "你好" + string2 + "\(10)"
    }
    func test8(string1:String,string2:String) {
        var string = string1
        string = string + "你好"
        string = string + string2
        string = string + "\(10)"
        
    }
屏幕快照 2017-05-01 22.59.05.png

test7表達(dá)式雖清晰,但復(fù)雜,編譯耗時(shí)23.4ms,test8將test7的表達(dá)式拆成幾部分,編譯時(shí)間1.3ms,表達(dá)式越簡(jiǎn)單,編譯時(shí)間越短,因此是編寫簡(jiǎn)潔明了的表達(dá)式,還是編寫對(duì)編譯器友好的表達(dá)式,我們是需要權(quán)衡的。

5.函數(shù)放在extension中,比不放在extension中編譯更耗時(shí),使用閉包也比較耗時(shí)。

二 編譯器層面優(yōu)化編譯時(shí)間#

  1. WHO
    簡(jiǎn)單地說,Whole-Module Optimization(全模塊優(yōu)化,以下簡(jiǎn)稱 WMO),即在編譯項(xiàng)目時(shí),將同屬于一個(gè) Module(可以理解為一個(gè) Target、一個(gè) Package)的所有源代碼都串起來,進(jìn)行整體的一個(gè)分析與優(yōu)化,區(qū)別于 Single-File Optimization(單文件優(yōu)化,以下簡(jiǎn)稱 SFO),WMO 可以更好的統(tǒng)籌全局,去 inline 函數(shù)調(diào)用、排除死函數(shù)(即寫了卻從不調(diào)用的函數(shù))等等,使編譯速度加快。但問題來了,WMO 只是在 Release 模式下成為了默認(rèn)且推薦的選項(xiàng),在 Debug 模式下默認(rèn)依然是 None。

2.利用Uber團(tuán)隊(duì)在利用swift3重寫客戶端中發(fā)現(xiàn)的黑科技
Uber 的開發(fā)團(tuán)隊(duì)偶然發(fā)現(xiàn)如果把所有 Model 文件全部合并到一個(gè)文件去編譯, 那編譯時(shí)間會(huì)從 1min 35s 減少到 17s, 那么我們?nèi)绻阉写a文件都合并到一起, 那就可以極大地優(yōu)化編譯速度了。
WHO(Whole-Module-Optimization) 也會(huì)把文件合并起來再進(jìn)行編譯, 實(shí)際使用時(shí)我們發(fā)現(xiàn)編譯雖然快了, 但對(duì)于編譯時(shí)間的減少還是遠(yuǎn)沒有直接把文件合并到一起那么有效. 主要原因是因?yàn)?WHO 除了合并文件之外, 還會(huì)在預(yù)編譯階段做這些事情: 檢測(cè)沒有被調(diào)用的方法和類型, 在預(yù)編譯期去掉它們,給沒有被繼承的類, 沒有被繼承的方法加上 final 標(biāo)簽, 給編譯器提供更多信息, 以便這些方法被優(yōu)化為靜態(tài)調(diào)用或者是內(nèi)聯(lián)進(jìn)去,這些優(yōu)化會(huì)對(duì)于程序的效率有很大的提升, 但編譯時(shí)間會(huì)有所增加。

Uber 的團(tuán)隊(duì)發(fā)現(xiàn)通過增加一個(gè)編譯宏就可以做到只合并文件, 而不做優(yōu)化. 進(jìn)入工程文件設(shè)置 -> Build Setting -> Add User-Defined Settings, key 為 SWIFT_WHOLE_MODULE_OPTIMIZATION
, value 設(shè)為 YES
, 然后把優(yōu)化級(jí)別設(shè)為 None
就可以了.

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,242評(píng)論 25 708
  • 發(fā)現(xiàn) 關(guān)注 消息 iOS 第三方庫、插件、知名博客總結(jié) 作者大灰狼的小綿羊哥哥關(guān)注 2017.06.26 09:4...
    肇東周閱讀 12,229評(píng)論 4 61
  • 純手工打造每一篇開源資訊與技術(shù)干貨,數(shù)十萬程序員和Linuxer已經(jīng)關(guān)注。 Bilby作為企業(yè)基礎(chǔ)設(shè)施保護(hù)團(tuán)隊(duì)中保...
    塵世不擾閱讀 201評(píng)論 0 1
  • 前幾天,你突然從共同的好友群里向我發(fā)起了對(duì)話,第一句話就是問我過得還好嗎,我第一反應(yīng)是忽略你,想把我的注意力集中在...
    浮生何歡閱讀 677評(píng)論 0 0
  • 初學(xué)ios接觸的都是新的,英文不好,看文檔都費(fèi)勁。所以要學(xué)習(xí)英語了 學(xué)習(xí)ios一個(gè)月了,今天整理一下ios開發(fā)中的...
    展翅飛鵬閱讀 260評(píng)論 0 0