Swift之通過(guò)減少動(dòng)態(tài)派發(fā)來(lái)提升性能


Swift是OO(面向?qū)ο螅┑恼Z(yǔ)言熊响,所以少不了方法和屬性的重載等特性缠捌,程序只能在運(yùn)行時(shí)來(lái)確定具體的方法或?qū)傩詠?lái)間接調(diào)用或間接訪問(wèn),這就叫做動(dòng)態(tài)派發(fā)轨功。從性能上考慮,對(duì)于動(dòng)態(tài)派發(fā)的方法傅物,會(huì)有常量時(shí)間的運(yùn)行時(shí)開銷夯辖。接下來(lái)將介紹三種方法來(lái)移除這樣的動(dòng)態(tài)性琉预,final董饰,private,全模塊優(yōu)化(Whole Module Optimization),以此提升性能卒暂。

考慮下面的例子:

class ParticleModel {
    var point = ( 0.0, 0.0 )
    var velocity = 100.0

    func updatePoint(newPoint: (Double, Double), newVelocity: Double) {
        point = newPoint
        velocity = newVelocity
    }

    func update(newP: (Double, Double), newV: Double) {
        updatePoint(newP, newVelocity: newV)
    }
}

var p = ParticleModel()
for i in stride(from: 0.0, through: 360, by: 1.0) {
    p.update((i * sin(i), i), newV:i*1000)
}

如上述代碼所示啄栓,調(diào)用過(guò)程為:

  1. 調(diào)用變量pupdate方法。
  2. 調(diào)用pupdatePoint方法也祠。
  3. 獲取p的元組類型變量point昙楚。
  4. 獲取p的屬性velocity。

由于ParticleModel可以被子類诈嘿,所以其方法和屬性就能被重載堪旧,這就不可避免的需要使用動(dòng)態(tài)調(diào)用。

在Swift中奖亚,動(dòng)態(tài)調(diào)用是通過(guò)在一個(gè)方法表中找到方法然后執(zhí)行間接的調(diào)用(類似于C++的虛函數(shù)表)淳梦,對(duì)于這種先查找再調(diào)用的過(guò)程,其效率是要低于方法的直接調(diào)用昔字,而且間接調(diào)用會(huì)阻止許多編譯器優(yōu)化爆袍,這將加重間接調(diào)用的開銷。接下來(lái)將列舉一些技巧來(lái)禁用動(dòng)態(tài)派發(fā)的行為作郭,以達(dá)到提升性能的目的陨囊。

當(dāng)屬性、方法夹攒、或類不需要被重載時(shí)蜘醋,可在其聲明的地方加上final關(guān)鍵字

在屬性,方法或類聲明時(shí)加上final關(guān)鍵字芹助,表示其不能被重載堂湖,這將允許編譯器安全的移除動(dòng)態(tài)派發(fā)。如下代碼所示状土,pointvelocity將直接從對(duì)象的存儲(chǔ)屬性中加載无蜂,updatePoint()方法將被直接調(diào)用;另外蒙谓,update()依然會(huì)通過(guò)動(dòng)態(tài)派發(fā)的方式來(lái)調(diào)用斥季,這樣,ParticleModel的子類就可以重載update()來(lái)自定義實(shí)現(xiàn)累驮。

class ParticleModel {
    final var point = ( x: 0.0, y: 0.0 )
    final var velocity = 100.0

    final func updatePoint(newPoint: (Double, Double), newVelocity: Double) {
        point = newPoint
        velocity = newVelocity
    }

    func update(newP: (Double, Double), newV: Double) {
        updatePoint(newP, newVelocity: newV)
    }
}

除了上面所示酣倾,在屬性和方法聲明前加final關(guān)鍵字,還可以直接在類上加final谤专,表示該類將不能作為父類被子類化躁锡,隱含的表明該類的所有的方法和屬性都是final的。

final class ParticleModel {
    var point = ( x: 0.0, y: 0.0 )
    var velocity = 100.0
    // ...
}

在屬性置侍、方法映之、或類聲明前加private關(guān)鍵字拦焚,將限制其只能在同一個(gè)文件中被引用

在聲明前加private關(guān)鍵字,將限制其只能在當(dāng)前文件中被引用杠输,這將允許編譯器在當(dāng)前文件中找到所有潛在的重載聲明赎败,編譯器會(huì)對(duì)這些private關(guān)鍵字的方法或?qū)傩赃M(jìn)行優(yōu)化,移除間接的方法調(diào)用以及屬性訪問(wèn)蠢甲。

假設(shè)在當(dāng)前文件中沒(méi)有類重載ParticleModel僵刮,那么編譯器將移除所有帶有private聲明的動(dòng)態(tài)派發(fā)調(diào)用。

class ParticleModel {
    private var point = ( x: 0.0, y: 0.0 )
    private var velocity = 100.0

    private func updatePoint(newPoint: (Double, Double), newVelocity: Double) {
        point = newPoint
        velocity = newVelocity
    }

    func update(newP: (Double, Double), newV: Double) {
        updatePoint(newP, newVelocity: newV)
    }
}

如上代碼所示鹦牛,pointvelocity將直接訪問(wèn)搞糕,updatePoint()方法也將直接被調(diào)用,而update()方法由于沒(méi)有加private關(guān)鍵字曼追,依然是只能間接調(diào)用寞宫。
同樣,private可以加在類的聲明前拉鹃,等同于類的所有方法和屬性都將加上private關(guān)鍵字辈赋。

private class ParticleModel {
    var point = ( x: 0.0, y: 0.0 )
    var velocity = 100.0
    // ...
}

在使用internal的聲明中通過(guò)使用Whole Module Optimization來(lái)隱式的推斷出final

默認(rèn)的情況下,Xcode將單獨(dú)編譯源文件膏燕,這會(huì)限制編譯器優(yōu)化的程度钥屈,Xcode 7后,增加了Whole Module Optimization選項(xiàng)坝辫,它能允許編譯器在同一個(gè)模塊(Module)中分析所有的源文件來(lái)進(jìn)行優(yōu)化篷就,可以在Xcode的Building Settings中開啟該選項(xiàng),如下圖所示近忙。

在開啟Whole Module Optimization選項(xiàng)竭业,且聲明為internal(默認(rèn)級(jí)別)的情況下,模塊的所有文件將同時(shí)被編譯及舍,這將允許編譯器對(duì)整個(gè)模塊一起分析未辆,并對(duì)沒(méi)有被重載且聲明為internal級(jí)別的類、方法或?qū)傩蕴砑?code>final關(guān)鍵字锯玛。
如下代碼所示咐柜,我們修改一下ParticleModel類,添加public關(guān)鍵字:

public class ParticleModel {
   var point = ( x: 0.0, y: 0.0 )
   var velocity = 100.0

   func updatePoint(newPoint: (Double, Double), newVelocity: Double) {
       point = newPoint
       velocity = newVelocity
   }

   public func update(newP: (Double, Double), newV: Double) {
       updatePoint(newP, newVelocity: newV)
   }
}

var p = ParticleModel()
for i in stride(from: 0.0, through: times, by: 1.0) {
   p.update((i * sin(i), i), newV:i*1000)
}

如上代碼攘残,當(dāng)開啟Whole Module Optimization選項(xiàng)的情況下拙友,編譯器能在屬性point,velotity,以及updatePoint()方法上推斷出final歼郭,既相當(dāng)于在point遗契、velocityupdatePoint()聲明前加上final關(guān)鍵字病曾,而update()方法由于是public級(jí)別牍蜂,所以無(wú)法推斷出final關(guān)鍵字涉瘾,其仍將是間接調(diào)用。

總結(jié):

  • 當(dāng)使用privatefinal關(guān)鍵字捷兰,或者在開啟Whole Module Optimization選項(xiàng),聲明為internal級(jí)別的沒(méi)有被重載的方法下负敏,將直接調(diào)用贡茅,在編譯時(shí)確定。
  • 運(yùn)行時(shí)決定的動(dòng)態(tài)派發(fā)的情形包括:
    • 繼承自NSObject或者方法有@objc前綴其做。
    • 使用Swift的方法表的方式顶考,除去上述情況下,將采用這種方式妖泄。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末驹沿,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子蹈胡,更是在濱河造成了極大的恐慌渊季,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,290評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件罚渐,死亡現(xiàn)場(chǎng)離奇詭異却汉,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)荷并,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,107評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門合砂,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人源织,你說(shuō)我怎么就攤上這事翩伪。” “怎么了谈息?”我有些...
    開封第一講書人閱讀 156,872評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵缘屹,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我侠仇,道長(zhǎng)囊颅,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,415評(píng)論 1 283
  • 正文 為了忘掉前任傅瞻,我火速辦了婚禮踢代,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘嗅骄。我一直安慰自己胳挎,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,453評(píng)論 6 385
  • 文/花漫 我一把揭開白布溺森。 她就那樣靜靜地躺著慕爬,像睡著了一般窑眯。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上医窿,一...
    開封第一講書人閱讀 49,784評(píng)論 1 290
  • 那天磅甩,我揣著相機(jī)與錄音,去河邊找鬼姥卢。 笑死卷要,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的独榴。 我是一名探鬼主播僧叉,決...
    沈念sama閱讀 38,927評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼棺榔!你這毒婦竟也來(lái)了瓶堕?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,691評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤症歇,失蹤者是張志新(化名)和其女友劉穎郎笆,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體忘晤,經(jīng)...
    沈念sama閱讀 44,137評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡题画,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,472評(píng)論 2 326
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了德频。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片苍息。...
    茶點(diǎn)故事閱讀 38,622評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖壹置,靈堂內(nèi)的尸體忽然破棺而出竞思,到底是詐尸還是另有隱情,我是刑警寧澤钞护,帶...
    沈念sama閱讀 34,289評(píng)論 4 329
  • 正文 年R本政府宣布盖喷,位于F島的核電站,受9級(jí)特大地震影響难咕,放射性物質(zhì)發(fā)生泄漏课梳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,887評(píng)論 3 312
  • 文/蒙蒙 一余佃、第九天 我趴在偏房一處隱蔽的房頂上張望暮刃。 院中可真熱鬧,春花似錦爆土、人聲如沸椭懊。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)氧猬。三九已至背犯,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間盅抚,已是汗流浹背漠魏。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留妄均,地道東北人柱锹。 一個(gè)月前我還...
    沈念sama閱讀 46,316評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像丛晦,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子提陶,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,490評(píng)論 2 348

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

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法烫沙,類相關(guān)的語(yǔ)法,內(nèi)部類的語(yǔ)法隙笆,繼承相關(guān)的語(yǔ)法锌蓄,異常的語(yǔ)法,線程的語(yǔ)...
    子非魚_t_閱讀 31,598評(píng)論 18 399
  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理撑柔,服務(wù)發(fā)現(xiàn)瘸爽,斷路器,智...
    卡卡羅2017閱讀 134,629評(píng)論 18 139
  • 對(duì)象的創(chuàng)建與銷毀 Item 1: 使用static工廠方法铅忿,而不是構(gòu)造函數(shù)創(chuàng)建對(duì)象:僅僅是創(chuàng)建對(duì)象的方法剪决,并非Fa...
    孫小磊閱讀 1,967評(píng)論 0 3
  • 前言 人生苦多,快來(lái) Kotlin 檀训,快速學(xué)習(xí)Kotlin柑潦! 什么是Kotlin? Kotlin 是種靜態(tài)類型編程...
    任半生囂狂閱讀 26,163評(píng)論 9 118
  • 估計(jì)最近用眼過(guò)多峻凫,白天上班看電腦渗鬼,晚上還要盯手機(jī),雙眼又脹又癢荧琼,甚至引起了頭昏頭疼譬胎。就這樣還要參與各類年終聚會(huì),我...
    磚兒zr閱讀 280評(píng)論 16 13