開啟 Cocoapods 新選項,加快專案索引速度

四娘發表於2019-05-30

前幾天 Cocoapods 1.7.0 正式版釋出了,我最期待的一個功能是 Multiple Pod Projects,昨天順手就給接入了,專案解析和索引效率有了非常明顯的提升,過程中踩了些坑,這次一起把之前 debug 的經驗分享一下。

generate_multiple_pod_projects 選項

之前 Cocoapods 會把每個依賴作為 target 放到 Pods 專案裡,但 xcodeproj 本身的編碼不太能適應這種情況,在引入幾十個 pod 的情況下,專案解析的效率會急劇下降。

以我公司其中一個主專案為例,Pods 專案的大小達到了 5.2 MB(這可都是純文字),在第一次開啟專案,解析專案構建索引時,就能明顯聽到風扇開始狂轉,這個過程會持續好幾分鐘才會結束。

Cocoapods 這次更新引入了一個 generate_multiple_pod_projects 的選項,可以讓每個依賴都作為一個單獨的專案引入,大大增加了解析速度:

開啟 Cocoapods 新選項,加快專案索引速度

開啟的方式很簡單,只要在 Podfile 里加入這一行就可以了:

install! 'cocoapods', generate_multiple_pod_projects: true
複製程式碼

拆分後每個專案的大小都差不多是 40 - 100 kb 左右:

開啟 Cocoapods 新選項,加快專案索引速度

這個選項開啟之後的效果非常顯著,我在 Xcode 裡執行了 clean,之後 indexing 的過程在幾秒鐘裡就結束了,而且風扇也沒有狂轉。

至於為什麼這樣可以提升專案的解析速度,我大概看了一下 xcodeproj 的編碼,所有的 Item 都會按照類別存放到各自的 section 裡,最終在專案的結構樹裡會以引用的形式呈現。

所以檔案引用查詢的範圍是所有 Pod 引用庫的檔案的集合,而每次索引的構建都至少會遍歷一次專案樹,這就會導致索引時間的暴增,除此之外單個龐大的專案解析也不利於多執行緒執行,拆分成多個專案的話就能有效地解決這些問題。

install! 函式只能呼叫一次

需要注意 install! 是個用來配置的函式,由於之前我還開啟了另一個選項,所以接入時是這麼做的:

install! 'cocoapods', generate_multiple_pod_projects: true
install! 'cocoapods', disable_input_output_paths: true
複製程式碼

但是這麼做之後發現不生效,後來才想起來 install! 是一個用來配置的函式,重複呼叫的話,只會以最後一次的呼叫為準。所以應該在一次呼叫裡把它們都傳入進去:

install! 'cocoapods', 
         disable_input_output_paths: true,
         generate_multiple_pod_projects: true
複製程式碼

Swift 版本控制

另一個坑就是在 post_install 時,為了一些版本的相容,需要遍歷所有 target,調整一部分庫的 Swift 版本:

post_install do |installer|

  swift_4_0_compatible = [ ... ]
  swift_4_2_compatible = [ ... ]

  installer.pod_targets.each do |t|
    t.build_configurations.each do |c|
      c.build_settings['SWIFT_VERSION'] = '4.0' if swift_4_0_compatible.include? t.name
      c.build_settings['SWIFT_VERSION'] = '4.2' if swift_4_2_compatible.include? t.name
    end
  end
end
複製程式碼

但是如果開啟了 generate_multiple_pod_projects 的話,由於專案結構的變化,installer.pod_targets 就沒辦法獲得所有 pods 引入的 target 了。

Podfile 裡的程式碼如何 debug

查了 Xcodeproj 和 Cocoapods 的文件之後我都沒有得到很好的解答,所以我就想用 xcodeproj 本身的介面去處理這件事情。

由於 Podfile 本質上是 Ruby 指令碼,所以這裡我通常會使用 Ruby 的 debugger 去操作,通過 Ruby 強大的自省能力,在 debugger 裡進行嘗試然後找到我們需要的介面,開始之前我們需要安裝一個 Ruby 的工具,步驟如下:

  1. 首先是安裝 debugger gem install pry
  2. 接著在 Podfile 的開頭匯入 require 'pry'
  3. 然後在我們想要插入斷點的地方插入 binding.pry 語句就可以了

查詢能用的介面

我在 post_install 裡插入了斷點,接著執行 pod install,就看到斷點生效了:

開啟 Cocoapods 新選項,加快專案索引速度

Ruby 的自省能力非常強大,而且 pry 也基於此做了很多實用的功能,在這裡我直接輸入了 installer 回車,就能看到它所有屬性都被遞迴列印出來。

這裡面我找了一下之後,發現一個文件裡沒有記錄的屬性,叫做 pod_target_subprojects,包含了所有 Pods 的專案,似乎可以滿足我們的需求:

開啟 Cocoapods 新選項,加快專案索引速度

接著 Ctrl + d 退出 pry,回到 Podfile 修改即可:

post_install do |installer|

  swift_4_0_compatible = [ ... ]
  swift_4_2_compatible = [ ... ]

  installer.pod_target_subprojects.flat_map { |p| p.targets }.each do |t|
    t.build_configurations.each do |c|
      c.build_settings['SWIFT_VERSION'] = '4.0' if swift_4_0_compatible.include? t.name
      c.build_settings['SWIFT_VERSION'] = '4.2' if swift_4_2_compatible.include? t.name
    end
  end
end
複製程式碼

最後 pod install 一下,開啟 Xcode 檢視對應的 target 的編譯設定,確實有效。

這裡介紹的 debug 方法在 fastlane 裡也適用,非常建議大家在編寫複雜指令碼時先用 debugger 去提前踩坑。

結語

用慣了 Ruby 的 debug 方式之後,回到 LLDB 感覺體驗差了很多?。

如果覺得文章還不錯的話可以關注一下我的部落格

相關文章