我們都知道托管代碼剝離是構(gòu)建游戲作品過(guò)程中的關(guān)鍵步驟。該步驟有助于減小應(yīng)用程序二進(jìn)制文件的大小,。它通過(guò)移除未使用的代碼來(lái)實(shí)現(xiàn),。
通過(guò)刪除代碼,用戶可以確保這些代碼不會(huì)被編譯或包含在最終的構(gòu)建中,。雖然這會(huì)稍微減少使用IL2CPP后端運(yùn)行項(xiàng)目的內(nèi)存使用,,但是在運(yùn)行時(shí),在較高的托管代碼剝離級(jí)別下,,總會(huì)有丟失類型和方法的風(fēng)險(xiǎn),。
在整個(gè)構(gòu)建過(guò)程中,經(jīng)常有一些代碼是無(wú)效的,,這些無(wú)效代碼會(huì)被剝離,。手動(dòng)將所需的程序集添加到link.xml文件不是防止它們被移除的最簡(jiǎn)單方法。在本文中將為大家介紹這些技巧和最佳的應(yīng)用方法,,以便大家有效改進(jìn)托管代碼剝離工作流,。
使用Unity鏈接器剝離托管代碼
當(dāng)我們使用IL2CPP腳本后端時(shí),刪除未使用的代碼尤其重要,。Unity鏈接器——Mono IL linker將執(zhí)行靜態(tài)分析以剝離托管代碼,。
Unity支持IL2CPP低、中,、高三個(gè)級(jí)別的托管代碼剝離,。托管代碼剝離指導(dǎo)手冊(cè)解釋了代碼剝離過(guò)程是如何工作的,哪些因素剝離了某些代碼,,以及剝離級(jí)別如何彼此不同,。簡(jiǎn)而言之:代碼剝離級(jí)別越高,鏈接器就越難找到并移除未使用的代碼,。因此我們可以在項(xiàng)目的“播放器設(shè)置”中修改“受管理的剝離級(jí)別”以快速找到那些被視為未使用的代碼,。
鏈接器利用靜態(tài)分析來(lái)識(shí)別未使用的代碼,,當(dāng)某個(gè)對(duì)象的類型只在運(yùn)行時(shí)定義時(shí),靜態(tài)分析不能覆蓋所有情況,。這些案例會(huì)導(dǎo)致虛假的積極性結(jié)果,。雖然編譯時(shí)可能沒(méi)有任何對(duì)類或方法的引用,但運(yùn)行時(shí)代碼的某些部分仍然需要該類或方法,。在這種情況下,,使用反射是一個(gè)很好的例子。
雖然這是一種有效且常用的代碼類型,,但鏈接器不知道MyAssembly,、MyType和MyMethod是否在運(yùn)行時(shí)實(shí)際使用。這可能會(huì)導(dǎo)致它們被剝離,,進(jìn)而導(dǎo)致運(yùn)行時(shí)錯(cuò)誤,。
使用依賴注入框架(如Zenject)或序列化庫(kù)(如Newtonsoft)的開(kāi)發(fā)人員必須意識(shí)到假陽(yáng)性代碼剝離是可能的。以下是一些最常見(jiàn)的解決方法:
鏈接器可以識(shí)別許多屬性,,并允許用戶在無(wú)法識(shí)別它們時(shí)對(duì)依賴項(xiàng)進(jìn)行注釋,。因此,用戶可以添加屬性不應(yīng)被分離的程序集,、類和方法,。
link.xml文件是一個(gè)針對(duì)每個(gè)項(xiàng)目的列表,記錄了保留程序集,、類型和其他代碼,,用戶可以手動(dòng)添加所需的程序集、類型和方法到link.xml,,或者使用UnityEditor APIgenerated additionallinkxmlfile在構(gòu)建過(guò)程中生成link.xml文件,。
可尋址包也利用了LinkxmlGenerator,??蓪ぶ钒臉?gòu)建腳本會(huì)檢查組中的資產(chǎn)列表,并將資產(chǎn)使用的類型添加到link.xml文件中,。它還通過(guò)運(yùn)行時(shí)的反射在內(nèi)部添加了可尋址包使用的類型,。
Unity支持位于“資源”文件夾或其子文件夾中的多個(gè)link.xml文件。在構(gòu)建過(guò)程中,,鏈接器會(huì)合并和計(jì)算多個(gè)link.xml文件的條目,。
Unity 2020 LTS版中托管代碼剝離有什么新功能
使用屬性可能需要一些手動(dòng)操作。但是如果項(xiàng)目已經(jīng)在Unity 2020 LTS中,,我們可以使用許多新的托管代碼剝離注釋屬性來(lái)輕松,、精確地在代碼剝離過(guò)程中標(biāo)記不該刪除的程序集與類型。以下是一些方法:
Required AttributeUsagesAttribute:標(biāo)記屬性類型時(shí),,該類型的所有自定義屬性也將被標(biāo)記,,從而減少在高剝離級(jí)別工作時(shí)的復(fù)雜性,。
RequireDerivedAttribute:標(biāo)記類型時(shí),從該類型派生的所有類型都將被標(biāo)記,。
RequiredInterfaceAttribute:標(biāo)記類型時(shí),,將標(biāo)記指定類型的所有接口。
RequiredMemberAttribute:標(biāo)記類型時(shí),,其所有帶有[RequiredMember]的類型都將被標(biāo)記,。這使得代碼剝離更加精確,因?yàn)樗鼘⒆柚孤暶黝愋妥兊貌豢蓜冸x,。但是請(qǐng)注意,,如果不使用類型本身,盡管標(biāo)記了[RequiredMember]屬性,,Members也將被剝離,。
RequireImplementorsAttribute:標(biāo)記接口類型時(shí),將標(biāo)記該接口的所有類型,。因此沒(méi)有必要標(biāo)記每個(gè)接口,。但是如果接口沒(méi)有在代碼庫(kù)中的任何地方運(yùn)行,它仍然會(huì)被移除,,盡管它已經(jīng)被標(biāo)記為[RequiresImplementors]屬性,。
在Unity 2020.1和2020.2中,該工具已經(jīng)收到了與Mono IL?Linker匹配的API更新,。它現(xiàn)在可以檢測(cè)到一些簡(jiǎn)單的反射模式,,這意味著如果您已經(jīng)升級(jí)到Unity 2020 LTS,那么使用link.xml文件的理由就更少了,。
作為Unity 2021年目標(biāo)的一部分,,即讓創(chuàng)作者更容易向測(cè)試人員和玩家交付高質(zhì)量的作品,Unity一直專注于改進(jìn)代碼剝離工作流,。更具體地說(shuō),,在2021.2版本中Unity添加了一個(gè)名為“最小”的新托管剝離級(jí)別。這將是IL2CPP后端的默認(rèn)設(shè)置,。