關(guān)于Xcode編譯性能優(yōu)化的研究工作總結(jié)

參考::關(guān)于Xcode編譯性能優(yōu)化的研究工作總結(jié)

總的來說,對Xcode的Build Setting 進行配置選項的修改是最直接的編譯設(shè)置。本工作總結(jié)除了從Xcode本身的配置進行優(yōu)化以外,還從外部環(huán)境、Xcode插件以及外部硬件配置的編譯優(yōu)化進行了研究分析。


目錄


一、編譯時長優(yōu)化 Swift編譯優(yōu)化 Find Implicit Dependencies
二、編譯時長優(yōu)化 Architectures
三、編譯時長優(yōu)化 Precompile Prefix Header 預(yù)編譯頭文件
四、編譯時長優(yōu)化 Swift Compile - Code Generation Optimization Level
五、加載RAM磁盤編譯Xcode項目
六、編譯線程數(shù)和Debug Information Format
6.1、 提高XCode編譯時使用的線程數(shù)
6.2、 將Debug Information Format改為DWARF
七、Link-Time Optimizations 鏈接時優(yōu)化
八、加裝SSD固態(tài)硬盤
九、安裝包大小優(yōu)化 Asset Catalog Compiler - Options Optimization
十、安裝包大小優(yōu)化 Flatten Compiles XIB Files
十一、安裝包大小優(yōu)化 清理未被使用的圖片資源LSUnusedResources
十二、安裝包大小優(yōu)化 Deployment Postprocessing和Strip Linked Product
十三、安裝包大小優(yōu)化 Linking->Dead Code Stripping
十四、Injection for Xcode 高效Xcode編譯調(diào)試插件
14.1 Injection
14.2 Limitations of Injection(局限性)


一、編譯時長優(yōu)化 Swift編譯優(yōu)化 Find Implicit Dependencies

對所編譯項目的Scheme進行配置
Product > Scheme > Edit Scheme > Build
Build Opitions選項中,去掉Find Implicit Dependencies.


這里寫圖片描述

原理:
選中Find Implicit Dependencies時,編譯以下內(nèi)容:

  • 項目所有的文件
  • 被修改的frameworks文件
    未選中Find Implicit Dependencies時,編譯以下內(nèi)容:
  • 項目中必要的文件
  • 不會重新編譯frameworks文件,即時你對其中的文件做了修改

Test:
對不同設(shè)置下(是否選中Find Implicit Dependencies)的項目編譯時間進行比較。
注:每次編譯前 進行clean操作(shift + command + k),達到消除Xcode自身增量編譯帶來的干擾。

Result:
對手頭的兩個demo進行了編譯耗時的比較:


這里寫圖片描述

對于兩個不同的項目,該配置所帶來的編譯優(yōu)化效果并不一定都能體現(xiàn)。


這里寫圖片描述

對兩個工程的framework文件進行對比之后發(fā)現(xiàn),LoveFreshBeen的framework文件要比DXDoctor的少得多。所以應(yīng)用該配置時,DXDoctor編譯時所反映出來的效果會更明顯。

缺點分析:
在這個選項(Find Implicit Dependencies)被選中的情況下,即使你只是對項目進行了很細微的改變,項目中的所有資源文件都會被重新編譯一遍。也會對所有被改變的frameworks進行編譯。沒有選中這個選項時,只會對文件中的一些Swift文件進行編譯,編譯耗時會顯著的下降。只是,在這種模式下,你對frameworks中的文件所進行的修改將不會進行重新編譯。

結(jié)論:
視修改的項目文件的不同,對兩種Scheme進行選擇,擇一使用以提高編譯性能。

參考:Swift Slow Compile Times Fix


二、編譯時長優(yōu)化 Architectures

在Build Settings中,有個Architectures配置選項。


這里寫圖片描述

Architectures
是指定工程支持的指令集的集合,如果設(shè)置多個architecture,則生成的二進制包會包含多個指令集代碼,提及會隨之變大。

Valid Architectures
有效的指令集集合,Architectures與Valid Architectures的交集來確定最終的數(shù)據(jù)包含的指令集代碼。

Build Active Architecture Only
指定是否只對當(dāng)前連接設(shè)備所支持的指令集編譯,默認Debug的時候設(shè)置為YES,Release的時候設(shè)為NO。Debug設(shè)置為YES時只編譯當(dāng)前的architecture版本,生成的包只包含當(dāng)前連接設(shè)備的指令集代碼;設(shè)置為NO時,則生成的包包含所有的指令集代碼(上述的V艾力達Architecture與Architecture的交集)。所以為了更快的編譯速度,Debug應(yīng)設(shè)為YES,而Release應(yīng)設(shè)為NO。

注:Debug設(shè)置為YES時,如果連接的設(shè)備是arm64的(iPhone 5s,iPhone 6(plus)等),則Valid Architecture中必須包含arm64,否則編譯會出錯。這種模式下編譯出來的版本是向下兼容的,即:編譯出的armv6版本可在armv7版本上運行。

參考:蘋果官方“Xcode Build Setting Reference”
關(guān)于Xcode “Build Setting”中的Architectures詳解


三、編譯時長優(yōu)化 Precompile Prefix Header 預(yù)編譯頭文件

Build Setting > Apple LLVM 7.1 - Language


這里寫圖片描述

Xcode 6及之后版本默認不使用pch文件參與項目編譯,原因有二:

  • 去掉自動導(dǎo)入的系統(tǒng)框架類庫的頭文件們可以提高源文件的復(fù)用性,便于遷移;
  • 一個龐大的Prefix Header會增加Build耗時。

但對于原有項目應(yīng)用了pch文件的情況,就需要對Xcode的Build Setting進行配置以使用pch。

當(dāng)Precompile Prefix Header設(shè)為NO時,頭文件pch不會被預(yù)編譯,而是在每個用到它導(dǎo)入的框架類庫中編譯一次。每個引用了pch內(nèi)容的.m文件都要編譯一次pch,這會降低項目的編譯速度。

將Precompile Prefix Header設(shè)為YES時,pch文件會被預(yù)編譯,預(yù)編譯后的pch會被緩存起來,從而提高編譯速度。
需要編譯的pch文件在Prefix Header中注冊即可。

手動創(chuàng)建pch文件: xcode6中如何添加pch文件
參考 :Xcode Precompile Prefix Header淺析
Why isn’t ProjectName-Prefix.pch created automatically in Xcode 6?


四、編譯時長優(yōu)化 Swift Compile - Code Generation Optimization Level

Build Setting > Swift Compile - Code Generation > Optimization Level


這里寫圖片描述

Swift 編譯優(yōu)化選項,對手上的兩個demo進行了以下測試:


這里寫圖片描述

Debug None[-Onone] Release Fast[-O] 是Xcode在Debug模式下編譯Swift項目的最優(yōu)選項,通過測試可以看出,在默認配置情況下和自定義情況下的編譯耗時存在比較明顯的差異。


五、加載RAM磁盤編譯Xcode項目

DerivedData

Xcode會在文件系統(tǒng)中集中的緩存臨時信息。

每次對Xcode iOS項目進行clean、build或者在iOS虛擬機上launch,Xcode都會在DeriveData文件夾中進行讀寫操作。換句話說,就是將Derived Data的讀寫從硬盤移動到內(nèi)存中。
DeriveData文件夾中包含了所有的build信息、debug- 和 release- built targets以及項目的索引。當(dāng)遇到零散索引(odd index)問題(代碼塊補全工作不正常、經(jīng)常性的重建索引、或者運行項目緩慢)時,它可以有效地刪除衍生數(shù)據(jù)。刪除這個文件夾將會導(dǎo)致所有Xcode上的項目信息遭到破壞。

Step 1
將DeriveData下的文件刪除:

rm -rf ~/Library/Developer/Xcode/DerivedData/*

刪除的這些數(shù)據(jù),Xcode會在Build時重新寫入的。

Step 2
在~/Library/Developer/Xcode/DerivedData.上部署安裝2 GB大小的RAM磁盤。
進到~/Library/Developer/Xcode/DerivedData.

cd ~/Library/Developer/Xcode/DerivedData

創(chuàng)建2 GB的RAM磁盤(size的計算公式 size = 需要分配的空間(M) * 1024 * 1024 / 512):

hdid -nomount ram://4194304

此行命令后將會輸出RAM磁盤的驅(qū)動名字:/dev/diskN(N為數(shù)字)。

初始化磁盤:

newfs_hfs -v DerivedData /dev/rdiskN

有以下輸出:
Initialized /dev/rdisk3 as a 2 GB case-insensitive HFS Plus volume

安裝磁盤:

diskutil mount -mountPoint ~/Library/Developer/Xcode/DerivedData /dev/diskN

這會在已存在的DeriveData上安裝一個卷,用于隱藏舊的文件。這些文件仍會占據(jù)空間,但在移除RAM磁盤之前都無法訪問。

在重啟或從Finder中彈出RAM磁盤時,磁盤中的內(nèi)容將會消失。下次再創(chuàng)建磁盤時,Xcode將會重新構(gòu)建它的索引和你的項目中間文件。

創(chuàng)建虛擬磁盤后, 并不是直接占用掉所有分配的空間, 而是根據(jù)虛擬磁盤中的文件總大小來逐漸占用內(nèi)存.
注:如果創(chuàng)建的虛擬磁盤已滿, 會導(dǎo)致編譯的失敗. 此時清除掉Derived Data后重新編譯, 就算有足夠的空間也還是有可能會導(dǎo)致編譯失敗. 重啟Xcode可以解決此問題.

對手頭Demo進行編譯測試,由于編譯本身讀寫內(nèi)容較少,耗時較短,都在10s到20s之內(nèi),所以提速感覺不明顯,在1s到2s間(10%左右),也許應(yīng)用到較大的項目中會有比較好的體現(xiàn)。

參考: Reduce XCode build times
【iOS Tip】提高Xcode編譯速度


六、編譯線程數(shù)和Debug Information Format

6.1、 提高XCode編譯時使用的線程數(shù)

defaults write com.apple.Xcode PBXNumberOfParallelBuildSubtasks 8  

其后的數(shù)字為指定的編譯線程數(shù)。XCode默認使用與CPU核數(shù)相同的線程來進行編譯,但由于編譯過程中的IO操作往往比CPU運算要多,因此適當(dāng)?shù)奶嵘€程數(shù)可以在一定程度上加快編譯速度。


6.2、 將Debug Information Format改為DWARF

在工程對應(yīng)Target的Build Settings中,找到Debug Information Format這一項,將Debug時的DWARF with dSYM file改為DWARF。

這一項設(shè)置的是是否將調(diào)試信息加入到可執(zhí)行文件中,改為DWARF后,如果程序崩潰,將無法輸出崩潰位置對應(yīng)的函數(shù)堆棧,但由于Debug模式下可以在XCode中查看調(diào)試信息,所以改為DWARF影響并不大。

需要注意的是,將Debug Information Format改為DWARF之后,會導(dǎo)致在Debug窗口無法查看相關(guān)類類型的成員變量的值。當(dāng)需要查看這些值時,可以將Debug Information Format改回DWARF with dSYM file,clean(必須)之后重新編譯即可。

注:6.2 的解決方案為Xcode的默認設(shè)置,進行反向設(shè)置時,編譯速度改變不大;

參考:To speed up the XCode compile and link speed (200%+)


七、Link-Time Optimizations 鏈接時優(yōu)化

Apple LLVM 7.1 - Code Generation Link-Time Optimization


這里寫圖片描述

Link-Time Optimization執(zhí)行鏈接時優(yōu)化(LTO)。在Clang/LLVM領(lǐng)域,這意味著鏈接器獲得的是LLVM字節(jié)碼,而不是通常的目標(biāo)文件。這些字節(jié)碼在一種更抽象的層次上代表程序這里寫鏈接內(nèi)容的執(zhí)行過程,允許LTO得以進行,但是壞處是,仍然需要將他們轉(zhuǎn)換成機器代碼,在鏈接時需要額外的處理時間。

參數(shù)設(shè)為YES時,能夠優(yōu)化鏈接時間;目標(biāo)文件以LLVM二進制文件格式存儲,在鏈接期間,優(yōu)化了整個程序。

將其設(shè)為NO時,可以減少Link階段的時間。對于Link階段耗時較長的項目,整體編譯優(yōu)化體現(xiàn)較為明顯。


八、加裝SSD固態(tài)硬盤

固態(tài)硬盤傳輸速度能達到500MB/s,其中讀取速度達到400-600MB每秒,寫入速度達到200MB每秒。而傳統(tǒng)硬盤讀取速度極限也無法超越200MB每秒,寫入速度在100MB每秒左右。如果遇到非連續(xù)的散片數(shù)據(jù),SSD能體現(xiàn)出極快的讀寫速度。而傳統(tǒng)機械硬盤由于磁頭尋道等原因,傳輸速度偏慢。

SSD加快了程序的I/O速率,讀寫速度比普通硬盤快,從而提升Xcode的編譯速度。受限于各種硬件原因,沒有進行測試。

參考: SSD 硬盤能否明顯加快編譯的速度?
固態(tài)硬盤的好處,固態(tài)硬盤與普通硬盤的區(qū)別


九、安裝包大小優(yōu)化 Asset Catalog Compiler - Options Optimization

Build Setting > Asset Catalog Compiler - Options


這里寫圖片描述

在Optimization 優(yōu)化設(shè)置項有三個選項,不指定、time和Space。


這里寫圖片描述

Optimization nothing是Xcode默認的設(shè)置。
與預(yù)想的不同,在選擇Optimization time 時,編譯時長并沒有得到優(yōu)化。
但在Optimization space時,編譯耗時基本沒有波動,但編譯生成的app 大小有不小程度的優(yōu)化。


十、安裝包大小優(yōu)化 Flatten Compiles XIB Files

是否扁平化編譯XIB文件。


這里寫圖片描述

官方解釋是:指定是否在編譯時剝離nib文件以優(yōu)化它們的大小,設(shè)為YES時編譯出來的nib文件會被壓縮但是不能編輯。

Description: Boolean value. Specifies whether to strip a nib files to
reduce their size. The resulting nib file is more compact but is not
editable.

這里寫圖片描述

測試app大小的同時也對編譯耗時進行了測試,在兩種編譯模式下的編譯耗時基本沒有變化。

參考:What does the “Flatten compiled xib files” project build option “do”
官方文檔 Interface Builder Compiler Build Settings


十一、安裝包大小優(yōu)化 清理未被使用的圖片資源LSUnusedResources

這里寫圖片描述

項目的開發(fā)過程總是會經(jīng)歷較長期的迭代,不斷的添加功能的同時會引入大量的圖片資源。需求變更、業(yè)務(wù)邏輯修改等需要移除某些功能模塊時就會導(dǎo)致這些前期加入的圖片資源問價被忽略而遺留在編譯的安裝包中,長此以往會使得安裝包變得格外臃腫。特別是類似于手Q項目的開發(fā),開發(fā)人員多,產(chǎn)品迭代頻繁,開發(fā)時間緊俏,開發(fā)人員輪換等特點更有可能導(dǎo)致這樣的后果。

一個較為傳統(tǒng)的清理方法時將圖片資源的文件名一一復(fù)制粘貼到Xcode的全局變量查找中去查找該字符串,如果返回的結(jié)果為零,則該資源很有可能沒被使用。之所以是“很有可能”,是因為在代碼中,資源有時是通過字符串拼接的方式進行引用的。

在這里提供一個github上的開源工具 LSUnusedResources ,這個工具是對github上的另一個開源工具Unused的優(yōu)化改進(匹配速度、結(jié)果準(zhǔn)確性),作者針對源碼、Xib、Storyboard 和 plist 等文件,先全文搜索其中可能是引用了資源的字符串,然后用資源名和字符串做匹配,從而找出未被使用的資源,比Unused的查找速度要快得多。

使用起來也比較簡單:
1、將工程目錄路徑拷貝到Folder或通過Browse瀏覽文件目錄;
2、在Resource指定要查找的資源類型;
(經(jīng)過本人測試,發(fā)現(xiàn)該工具在未指定Resource類型時所查找出來的資源不是很準(zhǔn)確,列舉出 的資源事實上是正在使用的,所以我在測試時指定查找了png類型的文件。)
3、單擊Search以查閱結(jié)果。

注:為了避免對資源的誤刪操作,建議在該工具輸出結(jié)果后對結(jié)果中的資源名復(fù)制并在Xcode的全局查找中進行校驗。

下載安裝:LSUnusedResources.app.zip
Github地址:LSUnusedResources
參考鏈接:查找XCode工程中沒被使用的圖片資源


十二、安裝包大小優(yōu)化 Deployment Postprocessing和Strip Linked Product

Xcode中Strip Linked Product 的默認設(shè)置為YES,但是Deployment Postprocessing的默認設(shè)置為NO。在Deployment Postprocessing 是Deployment的總開關(guān),所以在打開這個選項之前 Strip Linked Product是不起作用的。

注:當(dāng)Strip Linked Product設(shè)為YES的時候,運行app,斷點不會中斷,在程序中打印[NSThread callStackSymbols]也無法看到類名和方法名。而在程序崩潰時,函數(shù)調(diào)用棧中也無法看到類名和方法名。

這里寫圖片描述

打開這兩個選項之后進行編譯,編譯出的安裝包大小有了較大程度的優(yōu)化:


這里寫圖片描述

參考:Xcode中和symbols有關(guān)的幾個設(shè)置


十三、安裝包大小優(yōu)化 Linking->Dead Code Stripping

將Dead Code Stripping 設(shè)置為YES 也能夠一定程度上對程序安裝包進行優(yōu)化,只是優(yōu)化的效果一般,對于一些比較小的項目甚至沒有什么優(yōu)化體現(xiàn),所以這里也就沒有上測試數(shù)據(jù)。

Dead Code Stripping 是對程序編譯出的可執(zhí)行二進制文件中沒有被實際使用的代碼進行Strip操作。

對于更深層次的解讀,在參考鏈接的文章里有詳細描述。
參考:Dead Code Stripping


十四、Injection for Xcode 高效Xcode編譯調(diào)試插件

14.1 Injection

github上的開源項目,Xcode插件。

對于iOS開發(fā)者來說,XCode有個另人十分難耐的特性——編譯時長的問題。也許工作的時候你能夠為自己找到一個閑下來喝杯咖啡的正當(dāng)?shù)慕杩?,然而,多次的調(diào)試編譯過程足以讓你喝上好多杯咖啡了。應(yīng)該說,Injection是iOS開發(fā)者的福音,它在很大程度上優(yōu)化了XCode的性能,提升了開發(fā)者的工作效率。

Injection能夠在app運行時動態(tài)地向Swift或者OC文件注入新代碼并且即時地呈現(xiàn)在運行中的模擬器的app上,從而達到提高程序編譯速度,提高開發(fā)效率的目的。開發(fā)者不需要重新編譯重新運行整個項目,這樣的優(yōu)化使得編譯周期從7秒縮短至1秒。從XCode的輸出臺來看,每次在進行代碼注入之后都只會編譯被注入了代碼的文件。這么一聽有點類似于增量編譯。

設(shè)想這樣一個場景,對于一個編譯啟動需要10分鐘的項目,如果你想對某個功能的動畫效果進行微調(diào),是否意味著你需要以至少10分鐘為一個調(diào)試周期去對你的改動進行測試,而injection則能夠在程序運行時動態(tài)的改動方法實現(xiàn),并呈現(xiàn)在模擬器或真機上。

Injection Github https://github.com/johnno1962/injectionforxcode ;
或者到這里去看看他的演示:https://www.youtube.com/watch?v=uftvtmyZ8TM;

對于Injection的安裝使用,可以到第一個鏈接里下載package。Injection團隊為開發(fā)者準(zhǔn)備了一套傻瓜式的配置流程,基本上都是單擊continue就行了,然后重啟你的Xcode。裝成功后你會看到product > injection plugin。此時你應(yīng)該已經(jīng)裝成功了。點擊 Product >Injection Plugin > Patch Project for Injection 選項, 之后插件會在main.m 中插入兩段代碼。

#ifdef DEBUG
static char _inMainFilePath[] = __FILE__;
static const char *_inIPAddresses[] = {"10.12.1.67", "127.0.0.1", 0};

#define INJECTION_ENABLED
#import "/tmp/injectionforxcode/BundleInjection.h"
#endif

這不會影響程序原有代碼,如果要還原,隨時可以通過點擊 Revert Injection’s Changes 選項來還原。你可以開搞了。

用一個demo做實驗,將project運行起來,在運行時對你的代碼進行改動,可以使用快捷鍵Ctrl + =快速運行。也可以在 product > injection plugin > inject and reset app。 你會發(fā)現(xiàn)你改動的代碼所在類的左上角有一個藍色的進度條,一秒不到的時間就能夠完成注入并運行在你的app上。當(dāng)然,你也能夠在你改動的代碼的方法里邊加上一個斷點,快捷鍵Ctrl + = ,你會發(fā)現(xiàn)運行時會停在你設(shè)定的breakpoint上。

對于Swift文件injection好像還不能做到完美支持,github上有相關(guān)的解釋,我還沒有深入的嘗試,有興趣的童鞋可以去看看,順便交流交流。
injection是Xcode IDE的一個擴展,允許你去對類的一個方法實現(xiàn)打補丁而不需要重啟app。官方的原理如下:

It performs this by parsing the build logs of the application to
determine how a source file was last compiled. With this it wraps the
result of re-compiling into a bundle which is injected into the
application using the dynamic loader. At this point there are two
versions of a class in the app, the original and a new modified
version from the bundle. The modified version is then “swizzled” onto
the original class so changes take effect.

(個人翻譯)它通過解析程序的編譯日志來確定最后一次編譯的源文件。通過動態(tài)加載程序把重新編譯的結(jié)果打包到被注入代碼的app中。此時有兩個版本的類應(yīng)用,最初的和一個新的修改版本的包。這個修改后的版本,被“swizzled到”原始類中而生效。

除此之外,injection插件還有一個參數(shù)調(diào)節(jié)器Tunable Parameters,對于UI開發(fā)來說是個利器。比如對顏色的確定,對字體大小的界定等等。運行app,然后對參數(shù)進行修改就能夠動態(tài)的進行調(diào)試了。直觀而且方便。


這里寫圖片描述

對于 Tunable Parameters的使用我還沒有涉足,它的使用目前僅限于Swift項目,還需要在項目中進行一些諸如添加頭部代碼的配置,有興趣的童鞋可以到這里了解:
https://github.com/johnno1962/injectionforxcode/blob/master/documentation/tunable_parameters.md
其實也不復(fù)雜,就是在新建一個main.m文件之后加上幾行代碼。
在使用injection時,一個新的Xcode項目文件將會在原本項目的文件里生成(iOSInjectionProject或OSXInjectionProject)。這個文件是用于存放那些被injecte的項目文件的,建議將其加入到.gitignore 中,直接忽略。

每一次的項目文件被injected,在injection項目目錄里的injectionCount.txt中的數(shù)字就會增加。它可以很直觀的告訴你通過injection進行了多少的文件改動。

如果你想在真機或Appcode上進行測試:
你需要做一些輕量級的配置:在你的main.m文件加上如下幾行代碼:

#ifdef DEBUG
static char _inMainFilePath[] = __FILE__;
static const char *_inIPAddresses[] = {"10.12.1.67", "127.0.0.1", 0};

#define INJECTION_ENABLED
#import "/tmp/injectionforxcode/BundleInjection.h"
#endif

這個配置也可以通過Product > Injection Plugin > Patch Project For Injection 來進行自動配置。對于Swift文件,你需要添加一個空的main.m文件來完成配置。

至于使用Appcode的盆友,可以上github上看看教程:
https://github.com/johnno1962/injectionforxcode


14.2 Limitations of Injection(局限性)

貼github原文:

There are limitations of course, largely centering around static
variables, static or global functions and their Swift equivalents.
Consider the following Objective-C code.


這里寫圖片描述
  • One potential problem is when the new version of the class is
    loaded, it comes with it’s own versions of static variables such as
    sharedInstance and the once token. After injection has occurred, this
    would generate a new singleton instance.

To prevent this, class methods with the prefix “shared” are not
swizzled on injection to support this common idiom.

  • It can be tough to look through all of the memory of a running
    application. In order to determine the classes and instances to call
    the injected callbacks on, Injection performs a “sweep” to find all
    objects in memory. Roughly, this involves looking at an object, then
    recursively looking through objects which it refers to. For example,
    the object’s instance variables and properties.
This process is seeded using the application’s delegate and all windows. Once all the in-memory reference are collected, Injection 
will then filter these references to ones that it has compiled and 
injected. Then sending them the messages referenced in the callbacks 
section.



If no references are found, Injection will look through all objects that are referred to via sharedInstance. If that fails, well, 
Injection couldn’t find your instance. This is one way in which you 
may miss callbacks in your app.
  • The function dispatch_on_main does not inject, as it has been
    statically linked into the application. It does however, inject by
    proxy in the case shown via the doSomething method. dispatch_on_main
    will have been linked locally to a version in the object file being
    injected.

injection作為Xcode的插件,還是有局限性的。
injection的作用域主要集中在靜態(tài)變量、靜態(tài)或全局函數(shù)及其Swift的當(dāng)量(按:Swift equivalents)。

以下是作者貼的示例代碼:


這里寫圖片描述

1)有一個潛在的問題,當(dāng)類的新版本被加載,它帶有自己的靜態(tài)變量版本sharedInstance和once標(biāo)記。發(fā)生injected后,將產(chǎn)生一個新的單一實例。

  To prevent this, class methods with the prefix "shared" are not swizzled on injection to support this common idiom.       
  以上這句我捉摸了很久還是沒有吃透。

2)它可以瀏覽所有的正在運行的應(yīng)用程序的內(nèi)存。為了確定類和實例能夠調(diào)用injectied的回調(diào),injection會執(zhí)行一次“掃描”,找到在內(nèi)存中的所有對象。粗略說,這涉及了一個對象,然后通過遞歸尋找它所指向的對象。例如對象的實例變量和內(nèi)容(properties)。

3)This process is seeded using the application’s delegate and all windows.(按:這個過程通過應(yīng)用程序的代理和所有的窗口)。一旦所有在內(nèi)存中的引用被收集,injection將會過濾這些它已經(jīng)編譯和injected的引用,。然后再將被引用信息的回調(diào)部分發(fā)送出去。

4)如果沒有找到引用注入的內(nèi)容,Injection將通過sharedInstance查找所有被涉及到的對象。如果沒有找到任何對象,那么,Injection將找不到你的實例。這會導(dǎo)致你無法在你的app中進行回調(diào)函數(shù)的調(diào)用。

5)函數(shù)dispatch_on_main無法被injected,因為它已被靜態(tài)地鏈接到應(yīng)用程序。但是,injection可以通過代碼示例里的doSomething方法進行inject。dispatch_on_main將會被鏈接到本地的在被injected對象文件的一個新版本中。

以上內(nèi)容參考:https://github.com/johnno1962/injectionforxcode

對于某些童鞋的疑問:injection的編譯效率與XCode自身的增量編譯有什么優(yōu)勢?我已經(jīng)在github上Issue了作者并得到了如下回復(fù):


這里寫圖片描述

但是具體到底能夠提升多少,這個有待進一步的測試。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容