決定app性能的兩個(gè)重要因素是啟動(dòng)時(shí)間和內(nèi)存占用挟纱。減少app可執(zhí)行文件的大小炼蹦,以及盡量減少APP啟動(dòng)后內(nèi)存的占用使得app可以以更快的速度啟動(dòng)以及啟動(dòng)后占用更少的內(nèi)存空間尿这。使用動(dòng)態(tài)庫(kù)而不是靜態(tài)庫(kù)可以減小app的可執(zhí)行文件的大小撞羽,還允許app推遲加載具有特殊功能的庫(kù)到需要時(shí)认罩,而不是在啟動(dòng)時(shí)就進(jìn)行加載稠歉,這個(gè)特性進(jìn)一步降低了啟動(dòng)時(shí)間掰担,并且使內(nèi)存的使用更加高效。
本文介紹了動(dòng)態(tài)庫(kù)怒炸,并展示了如何使用動(dòng)態(tài)庫(kù)而不是靜態(tài)庫(kù)來(lái)降低可執(zhí)行文件的大小和初始內(nèi)存占用带饱。
首先,先了解下什么是靜態(tài)庫(kù)阅羹。
應(yīng)用程序的大部分功能都是在可執(zhí)行代碼庫(kù)中實(shí)現(xiàn)的勺疼。當(dāng)應(yīng)用程序使用靜態(tài)鏈接器與庫(kù)鏈接時(shí),靜態(tài)庫(kù)中被app使用到的那部分代碼被拷貝到生成的可執(zhí)行文件中灯蝴。靜態(tài)連接器將編譯后的源代碼(即目標(biāo)代碼)以及app使用到的那部分靜態(tài)庫(kù)代碼收集到一個(gè)可執(zhí)行文件中恢口,該文件在運(yùn)行時(shí)會(huì)被全部加載到內(nèi)存中。成為app可執(zhí)行文件的一部分的庫(kù)稱之為靜態(tài)庫(kù)穷躁。靜態(tài)庫(kù)是目標(biāo)文件的集合或歸檔耕肩。
注意:靜態(tài)庫(kù)也稱為靜態(tài)歸檔庫(kù)、靜態(tài)鏈接共享庫(kù)问潭。
當(dāng)一個(gè)app啟動(dòng)時(shí)猿诸,應(yīng)用程序的代碼——包括與app鏈接的靜態(tài)庫(kù)的代碼,都被加載到應(yīng)用程序地址空間中狡忙。將許多靜態(tài)庫(kù)鏈接到app會(huì)導(dǎo)致app的可執(zhí)行文件非常大梳虽。圖一展示了使用靜態(tài)庫(kù)中實(shí)現(xiàn)的功能的app的內(nèi)存使用情況。如果app的可執(zhí)行文件很大灾茁,那么窜觉,會(huì)導(dǎo)致app啟動(dòng)變慢,以及更大的內(nèi)存占用北专。此外禀挫,當(dāng)某個(gè)靜態(tài)庫(kù)更新后,使用它的app將不會(huì)因?yàn)樵撿o態(tài)庫(kù)的更新而受益拓颓。為了獲得靜態(tài)庫(kù)改進(jìn)后的功能语婴,app開(kāi)發(fā)人員必須將app的目標(biāo)文件與新版本的靜態(tài)庫(kù)重新鏈接在一起。而且,使用該app的用戶也需要安裝新版本的app砰左。
那么匿醒,什么是動(dòng)態(tài)庫(kù)呢?
更好的方式是:app在真正需要的時(shí)候才將代碼加載到它的地址空間缠导,可以是在啟動(dòng)時(shí)廉羔,亦或是運(yùn)行時(shí)。具備這種靈活性的庫(kù)就是動(dòng)態(tài)庫(kù)酬核。動(dòng)態(tài)庫(kù)不會(huì)被靜態(tài)鏈接到使用它的app中蜜另,也不會(huì)成為可執(zhí)行文件的一部分适室。相反嫡意,動(dòng)態(tài)庫(kù)是在app啟動(dòng)或運(yùn)行時(shí)才被加載或鏈接。
注意:動(dòng)態(tài)庫(kù)也稱為動(dòng)態(tài)共享庫(kù)捣辆,共享對(duì)象或動(dòng)態(tài)鏈接庫(kù)蔬螟。
圖2展示了如何使用動(dòng)態(tài)庫(kù)而不是靜態(tài)庫(kù)來(lái)實(shí)現(xiàn)某些功能,從而降低app啟動(dòng)后所使用的內(nèi)存汽畴。
如果使用動(dòng)態(tài)庫(kù)旧巾,那么,程序可以自動(dòng)地受益于它所使用的某個(gè)動(dòng)態(tài)庫(kù)的更新忍些,因?yàn)閹?kù)是被動(dòng)態(tài)鏈接的鲁猩,而不是靜態(tài)鏈接。也就是說(shuō)罢坝,app的功能可以在不需要app開(kāi)發(fā)者重新編譯app的前提下廓握,進(jìn)行改進(jìn)和擴(kuò)展。
動(dòng)態(tài)庫(kù)還有另外一個(gè)好處嘁酿,就是當(dāng)他們?cè)谠诩虞d時(shí)隙券,可以被初始化;當(dāng)app正常終止時(shí)闹司,可以執(zhí)行清理任務(wù)娱仔。而靜態(tài)庫(kù)就沒(méi)有這個(gè)特性。
開(kāi)發(fā)動(dòng)態(tài)庫(kù)時(shí)游桩,有一個(gè)問(wèn)題開(kāi)發(fā)人員必須牢記于心牲迫,就是動(dòng)態(tài)庫(kù)在更新時(shí),要保持兼容性借卧。因?yàn)閯?dòng)態(tài)庫(kù)的更新并不需要了解app開(kāi)發(fā)者是如何使用該動(dòng)態(tài)庫(kù)的盹憎,app必須能夠使用庫(kù)的最新版本,而不必更改任何代碼谓娃。所以脚乡,動(dòng)態(tài)庫(kù)的API不應(yīng)該被修改。然而,有時(shí)候改進(jìn)必須要修改庫(kù)的API奶稠,在這種情況下俯艰,動(dòng)態(tài)庫(kù)的前一個(gè)版本必須保留在用戶計(jì)算機(jī)中,以便app可以正常運(yùn)行锌订。
動(dòng)態(tài)庫(kù)是如何被使用的呢竹握?
當(dāng)app啟動(dòng)時(shí),OS內(nèi)核將app的代碼和數(shù)據(jù)加載到一個(gè)新的進(jìn)程的地址空間中辆飘。內(nèi)核同時(shí)也加載動(dòng)態(tài)加載器(usr/lib/dyld)到進(jìn)程中啦辐。然后動(dòng)態(tài)加載器加載app的依賴庫(kù),也就是app鏈接的那些動(dòng)態(tài)庫(kù)蜈项。靜態(tài)鏈接器記錄下app所鏈接的每一個(gè)依賴庫(kù)的文件名芹关。此文件名被認(rèn)為是動(dòng)態(tài)庫(kù)的安裝名。動(dòng)態(tài)加載器利用app的依賴庫(kù)的安裝名紧卒,在文件系統(tǒng)中將它們進(jìn)行定位侥衬。如果動(dòng)態(tài)加載器在app啟動(dòng)時(shí)沒(méi)有找到所有的依賴庫(kù),或者任何一個(gè)庫(kù)與app不兼容跑芳,那么轴总,啟動(dòng)進(jìn)程將終止。
動(dòng)態(tài)加載器只是決定了app啟動(dòng)過(guò)程中實(shí)際使用到的未定義的外部符號(hào)博个。其它符號(hào)并沒(méi)有決定怀樟,直到app使用它們的時(shí)候才進(jìn)行決定。
動(dòng)態(tài)加載器除了在啟動(dòng)時(shí)自動(dòng)加載動(dòng)態(tài)庫(kù)以外盆佣,還會(huì)根據(jù)app的請(qǐng)求在運(yùn)行時(shí)加載動(dòng)態(tài)庫(kù)往堡。也就是說(shuō),如果app不需要在啟動(dòng)時(shí)加載動(dòng)態(tài)庫(kù)罪塔,那么開(kāi)發(fā)者可以選擇不將app的目標(biāo)文件和動(dòng)態(tài)庫(kù)進(jìn)行鏈接投蝉,而是只在app需要它的時(shí)候才加載動(dòng)態(tài)庫(kù)。
翻譯自蘋(píng)果官方文檔:https://developer.apple.com/library/content/documentation/DeveloperTools/Conceptual/DynamicLibraries/100-Articles/OverviewOfDynamicLibraries.html