iOS/flutter動態化雜談

iOS/flutter動態化雜談

爲何須要動態化?實際上運營需求卻是其次,更主要的是修復線上嚴重問題。html

那麼是否必定要動態化呢?倒也不必定,國外開發者彷佛更傾向於經過更好的機制來避免問題產生,搞更完善的CR/CI/單元測試之類的,而國內開發者更傾向於用動態化手段進行兜底。git

這種差別是客觀存在的,可能跟思惟方式和技術發展甚至市場環境都有必定關係。這裏咱們不去深究,做爲一個國內的iOS開發者,動態化是不得不關注技術問題。github


iOS平臺在面對動態化技術時是特殊的。由於它是惟一一個從系統層面禁止你下發執行動態庫的平臺,它只給安裝包中帶的可執行文件以執行權限,這在APP層面是沒法繞過的限制。正則表達式

所以其餘平臺只須要替換相關可執行文件便可。若是作了插件化就更容易了。小程序

所幸,Objc是個比較動態的語言,雖然沒有js等腳本語言那麼動態,可是它仍然能夠:工具

  • 運行時構造類和方法
  • 運行時替換方法的實現

這意味着咱們能夠作不少不少事情,離任意修改程序、執行任意邏輯已經很接近了,只差:實現新方法,也就是具體邏輯的實現。post

方法不少,最簡單的就是搞個腳本語言的解釋器,在新方法裏調用這個解釋器作邏輯。早期有個Wax的項目,用lua來搞。不事後來iOS自帶了JSCore,而且js語言受衆也比lua多不少,JSPatch逐漸替代了Wax。性能

在這種JS動態化的基礎上,衍生出了更多的方案。一類是RN/Weex,邏輯全用JS,渲染用Native組件來作;一類是小程序,邏輯也是用JS,渲染目前是Web+有限的Native組件,但要注意的是小程序的開發規範是不限制底層渲染的具體實現的,之後徹底能夠無感知地切換到別的方式進行渲染(某些平臺已經在嘗試了)。單元測試

這類基於JS的動態化方案有一些缺點:測試

  1. JS跟native相互調用的代價是比較大的。所以RN/Weex在某些場景下有沒法避免的性能瓶頸。
  2. 使用JSPatch時,因爲JS和Objc的語義是自然不徹底一致的,一些類型互轉、內存管理也可能須要注意。
  3. Native開發者不必定熟悉JS

由此衍生出兩套方案,一個是滴滴的,讓開發者寫Objc代碼,經過工具編譯成JS,再作下發,經過這種方式來減小業務開發者的成本;另外一個是騰訊手機QQ的,經過實現了一個Objc的解釋器來解決這個問題。

手機QQ團隊這套激進的動態化方案名爲OCS,參考OCS ——史上最瘋狂的 iOS 動態化方案

咱們前面提到,Objc自己比較動態,可是純邏輯的執行仍是須要一個載體的,前面講的是經過腳本語言來作,而OCS則是本身搞了一個虛擬機來作。固然這個虛擬機不須要實現完整的解釋執行Objc的能力,解析代碼並生成AST的過程能夠離線作,也就是寫好Patch代碼直接經過基於clang的工具編個AST出來打包進行下發;實際執行中Objc相關的部分能夠直接調用Runtime實現,這樣的話VM真正須要實現的主要就是些簡單的C語法。實際看起來效果,還行。由於避免了跨語言開銷,性能比起JSPatch有比較大的優點。

若是隻是熱補丁的話,這樣的性能優點意義並不大,所以OCS的主要出發點多是安裝包瘦身,經過它實現部分功能,對性能的要求就比較苛刻了。

這個方案能夠對照OCEval-動態執行ObjectiveC的熱修復方案這個項目看看,思路相似不過OCEval看起來是把語法分析部分也放在VM裏作的,目前好像還不是很是完善,但有很是好的參考價值。


總的來講,Objc因爲語言自己的動態性,雖然iOS官方不容許動態執行代碼,但仍是能夠作不少動態化的事情的。而Swift就不同了,因爲Swift是靜態語言(這裏暫不考慮Swift使用Objc runtime的狀況,畢竟不太可能對整個項目作這樣的限制),前面這些思路在Swift上幾乎沒法使用。即便咱們搞了一個完善的Swift的解釋器出來,也很難橋接原有代碼。

我是不知道Swift項目有什麼好的方案了。老老實實完善CR/CI流程,提升代碼質量吧。


再說一下Flutter的動態化,一樣,Dart做爲靜態語言,也是不太好作動態化的。

在其它平臺能夠直接替換產物的,算個Diff打個Patch就好,思路仍是比較簡單的,iOS是行不通的,畢竟Dart的AOT產物是可執行的機器碼。

閒魚給出了一種模板解析的方案,這個基本上就是實現一下動態的UI下發,也就能支持一下運營活動,對Hotfix是無能爲力的。

騰訊看點團隊搞了個MXFlutter方案,基本思路是用JS寫UI層邏輯,JS的邏輯反正是能夠動態替換的。先上結論,這就是個玩具,幾乎沒有落地的意義。想一想這裏的調用路徑,Dart - Native - JS的兩層跨語言調用,整個UI層都這麼搞,性能會不好;並且Widget tree由JS側生成,映射成JSON傳給Dart再解析回Widget Tree,這個性能消耗也蠻大的,而聲明式UI意味着這個build過程必然是頻繁調用的,看點給的解決方案...哈哈哈原諒我不厚道地笑了。

不過就算這些思路都不行,咱們還有最後的辦法:在Release時引入完整的DartVM。差很少就是發佈Flutter的Debug產物,這樣Engine大約要40M安裝包(含Dart VM),而Dart部分能夠動態下發。缺點一方面是佔安裝包比較大,另外一方面是Dart的JIT性能可能不如AOT,尤爲是剛啓動的階段。不過在網上找了個benchmark,看起來差得很少甚至不少方面JIT更好,參考Dart vs Dart aot

DartVM方案多是Flutter動態化的終極解決方案。


多BB兩句語言性能的問題,雖然理論上c這類直接編譯到機器碼的語言性能是真正的天花板,但實際上,基於VM的語言,因爲能夠獲得一些運行時信息,並作一些運行時編譯,實際性能並不會差不少,某些場景反而更容易優化。

好比Dart的這個issue:AOT code is 65% slower than JIT on dart_style benchmark,就是正則表達式的JIT優化更好。

相關文章
相關標籤/搜索