前言
在上文《編譯與鏈接過程的思考》評(píng)論中暴走大牙提到了靜態(tài)庫和動(dòng)態(tài)庫依賴的問題抱环,還在群里提了幾個(gè)測試樣例和測試工程范抓。
大致介紹下測試工程和如何進(jìn)行測試:
工程P為主工程腔彰,其中有4個(gè)子工程A沿癞、B等缀、C、D臣嚣,子工程打包的庫為動(dòng)態(tài)庫或靜態(tài)庫净刮,子工程之間存在依賴關(guān)系。
通過修改主工程的依賴庫硅则,以及子工程的依賴關(guān)系以及打包類型淹父,測試動(dòng)態(tài)庫依賴靜態(tài)庫、靜態(tài)庫依賴動(dòng)態(tài)庫怎虫、靜態(tài)庫依賴靜態(tài)庫的情況暑认。
正文
在測試之前,先簡單說明下靜態(tài)庫和動(dòng)態(tài)庫的打包方式
- Cocoa Touch Framework
- Mach-O Type 為 Static
打包的.framework文件為靜態(tài)庫 - Mach-O Type 為 Dynamic
打包的.framework文件為動(dòng)態(tài)庫 -
Cocoa Touch Static Library
打包的.a文件為靜態(tài)庫
靜態(tài)庫依賴靜態(tài)庫
測試環(huán)境
靜態(tài)庫A大审、B蘸际、C均采用Cocoa Touch Framework的打包方式。
- 靜態(tài)庫A:提供函數(shù)foo();
- 靜態(tài)庫B:提供函數(shù)call_foo_b(); 依賴靜態(tài)庫A徒扶,在call_foo_b中調(diào)用foo();
-
靜態(tài)庫C:提供函數(shù)foo()粮彤;
測試代碼
#include "BLib.h"
#include "CLib.h"
- (void)testLib {
NSLog(@"Test A.");
call_foo_b();
NSLog(@"Test B.");
foo();
}
測試結(jié)果
2016-12-20 09:54:12.931731 testLib[7671:4787567] Test A.
call_foo in BLib.
foo in ALib.
2016-12-20 09:54:12.931925 testLib[7671:4787567] Test B.
foo in ALib.
- 對于TestA,我們調(diào)用B的call_foo_b,然后在call_foo_b中又調(diào)用A的foo导坟,打印的調(diào)用順序?yàn)锽->A屿良,正常;
- 對于TestB惫周,我們引入C的頭文件尘惧,然后調(diào)用C的foo,打印的調(diào)用順序是A闯两,異常褥伴;
結(jié)果思考??
靜態(tài)庫的生成只有編譯,沒有鏈接漾狼;
當(dāng)工程同時(shí)存在庫A和C時(shí)重慢,兩個(gè)foo的函數(shù)符號(hào)在鏈接的時(shí)候,先引入者優(yōu)先逊躁;
驗(yàn)證:把工程依賴順序從ABC改成CBA之后似踱,結(jié)果輸出變?yōu)?br> 2016-12-20 10:19:28.613791 testLib[7691:4795943] Test A.
call_foo in BLib.
foo in CLib.
2016-12-20 10:19:28.613871 testLib[7691:4795943] Test B.
foo in CLib.
靜態(tài)庫依賴動(dòng)態(tài)庫
測試環(huán)境
庫A、B稽煤、C核芽、D均采用Cocoa Touch Framework的打包方式。
- 動(dòng)態(tài)庫A:提供函數(shù)foo();
- 靜態(tài)庫B:提供函數(shù)call_foo_b(); 依賴動(dòng)態(tài)庫A酵熙,在call_foo_b中調(diào)用foo();
- 動(dòng)態(tài)庫C:提供函數(shù)foo()轧简;
-
靜態(tài)庫D:提供函數(shù)call_foo_d(); 依賴動(dòng)態(tài)庫C,在call_foo_d中調(diào)用foo();
測試代碼
#include "BLib.h"
#include "DLib.h"
- (void)testLib {
NSLog(@"Test lib.");
call_foo_b();
call_foo_d();
}
測試結(jié)果
2016-12-20 10:36:09.389209 testLib[7707:4799800] Test lib.
call_foo in BLib.
foo in ALib.
call_foo in DLib.
foo in ALib.
- 對于第一組測試匾二,我們調(diào)用靜態(tài)庫B的函數(shù)call_foo_b哮独,在函數(shù)call_foo_b中調(diào)用動(dòng)態(tài)庫A的函數(shù),正常察藐;
- 對于第二組測試皮璧,我們調(diào)用靜態(tài)庫D的函數(shù)call_foo_d,在函數(shù)call_foo_d中調(diào)用動(dòng)態(tài)庫A的函數(shù)分飞,異常悴务;(預(yù)想中是調(diào)用動(dòng)態(tài)庫C的函數(shù))
結(jié)果思考??
靜態(tài)庫的生成只有編譯,沒有鏈接譬猫;
那么在靜態(tài)庫D生成的過程中讯檐,只是確定了靜態(tài)庫D需要用到動(dòng)態(tài)庫中的foo函數(shù);
當(dāng)運(yùn)行時(shí)染服,加載了動(dòng)態(tài)庫A裂垦、C,其中兩個(gè)庫均含有foo函數(shù)肌索;動(dòng)態(tài)鏈接器,按照加載的順序,取到動(dòng)態(tài)庫A中的foo函數(shù)诚亚;
所以靜態(tài)庫B晕换、D調(diào)用的foo函數(shù)均是動(dòng)態(tài)庫A中的foo函數(shù)。
驗(yàn)證:我們調(diào)換Link Binary With Libraries 中A和C的位置站宗,結(jié)果如下
2016-12-20 10:35:11.048034 testLib[7705:4799491] Test lib.
call_foo in BLib.
foo in CLib.
call_foo in DLib.
foo in CLib.
動(dòng)態(tài)庫依賴靜態(tài)庫
測試環(huán)境
庫A闸准、B、C梢灭、D均采用Cocoa Touch Framework的打包方式夷家。
- 靜態(tài)庫A:提供函數(shù)foo();
- 動(dòng)態(tài)庫B:提供函數(shù)call_foo_b(); 依賴靜態(tài)庫A,在call_foo_b中調(diào)用foo();
- 靜態(tài)庫C:提供函數(shù)foo()敏释;
-
動(dòng)態(tài)庫D:提供函數(shù)call_foo_d(); 依賴靜態(tài)庫C库快,在call_foo_d中調(diào)用foo();
測試代碼
#include "BLib.h"
#include "DLib.h"
- (void)testLib {
NSLog(@"Test lib.");
call_foo_b();
call_foo_d();
}
測試結(jié)果
2016-12-20 11:08:52.715415 testLib[7746:4810080] Test lib.
call_foo in BLib.
foo in ALib.
call_foo in DLib.
foo in CLib.
- 對于第一組測試,我們調(diào)用動(dòng)態(tài)庫B的函數(shù)call_foo_b钥顽,在函數(shù)call_foo_b中調(diào)用靜態(tài)庫A的函數(shù)义屏,正常;
- 對于第二組測試蜂大,我們調(diào)用動(dòng)態(tài)庫D的函數(shù)call_foo_d闽铐,在函數(shù)call_foo_d中調(diào)用靜態(tài)庫C的函數(shù),正常奶浦;
結(jié)果思考??
工程依賴?yán)锩嬷挥袆?dòng)態(tài)庫B兄墅、D,沒有靜態(tài)庫A澳叉、C隙咸;
靜態(tài)庫A、C同名函數(shù)foo沒有沖突耳高;
這兩個(gè)現(xiàn)象是原因是動(dòng)態(tài)庫在生成的過程中扎瓶,除了編譯還有鏈接的過程。如果動(dòng)態(tài)庫依賴靜態(tài)庫泌枪,在生成動(dòng)態(tài)庫時(shí)會(huì)將靜態(tài)庫的代碼合并到動(dòng)態(tài)庫中概荷。
擴(kuò)展
如果動(dòng)態(tài)庫B、D的函數(shù)名字使用一樣的call_foo碌燕,調(diào)用順序和Link Binary With Libraries相關(guān)误证,與embeded的順序無關(guān);(embeded只是把動(dòng)態(tài)庫放入bundle中修壕,關(guān)鍵在于鏈接器的順序)
動(dòng)態(tài)庫依賴動(dòng)態(tài)庫
測試環(huán)境
動(dòng)態(tài)庫A愈捅、B、C慈鸠、D均采用Cocoa Touch Framework的打包方式蓝谨。
- 動(dòng)態(tài)庫A:提供函數(shù)foo();
- 動(dòng)態(tài)庫B:提供函數(shù)call_foo_b(); 依賴動(dòng)態(tài)庫A,在call_foo_b中調(diào)用foo();
- 動(dòng)態(tài)庫C:提供函數(shù)foo();
-
動(dòng)態(tài)庫D:提供函數(shù)call_foo_d(); 依賴動(dòng)態(tài)庫C譬巫,在call_foo_d中調(diào)用foo();
測試代碼
#include "BLib.h"
#include "DLib.h"
- (void)testLib {
NSLog(@"Test lib.");
call_foo_b();
call_foo_d();
}
測試結(jié)果
2016-12-20 11:08:52.715415 testLib[7746:4810080] Test lib.
call_foo in BLib.
foo in ALib.
call_foo in DLib.
foo in CLib.
- 對于第一組測試咖楣,我們調(diào)用動(dòng)態(tài)庫B的函數(shù)call_foo_b,在函數(shù)call_foo_b中調(diào)用動(dòng)態(tài)庫A的foo函數(shù)芦昔,正常诱贿;
- 對于第二組測試,我們調(diào)用動(dòng)態(tài)庫D的函數(shù)call_foo_d咕缎,在函數(shù)call_foo_d中調(diào)用動(dòng)態(tài)庫C的foo函數(shù)珠十,正常;
結(jié)果思考??
四個(gè)動(dòng)態(tài)庫都需要Link和Embeded凭豪;
與靜態(tài)庫依賴動(dòng)態(tài)庫的測試樣例不同焙蹭,這次雖然動(dòng)態(tài)庫A、C存在同名函數(shù)foo墅诡,但是調(diào)用的時(shí)候沒有沖突壳嚎。
動(dòng)態(tài)庫依賴動(dòng)態(tài)庫,在生成動(dòng)態(tài)庫的時(shí)候不會(huì)把依賴的動(dòng)態(tài)庫合并到動(dòng)態(tài)庫中末早。
總結(jié)
靜態(tài)庫的生成只有編譯烟馅,沒有鏈接;
動(dòng)態(tài)庫的生成除了編譯還有鏈接的過程然磷;
如果動(dòng)態(tài)庫依賴靜態(tài)庫郑趁,在生成動(dòng)態(tài)庫時(shí)會(huì)將靜態(tài)庫的代碼合并到動(dòng)態(tài)庫中;
- 靜態(tài)庫A依賴靜態(tài)庫B姿搜,使用時(shí)需要在Link Binary With Libraries引入靜態(tài)庫A寡润、B;
- 靜態(tài)庫A依賴動(dòng)態(tài)庫B舅柜,使用時(shí)需要在Link Binary With Libraries引入靜態(tài)庫A和動(dòng)態(tài)庫B梭纹,并且在Embeded Binaries引入動(dòng)態(tài)庫B;
- 動(dòng)態(tài)庫A依賴靜態(tài)庫B致份,使用時(shí)需要在Link Binary With Libraries引入動(dòng)態(tài)庫A变抽,并且在Embeded Binaries引入動(dòng)態(tài)庫A;
- 動(dòng)態(tài)庫A依賴動(dòng)態(tài)庫B氮块,使用時(shí)需要在Link Binary With Libraries引入動(dòng)態(tài)庫A和B绍载,并且在Embeded Binaries引入動(dòng)態(tài)庫A和B;
所有的代碼都可以在這里找到滔蝉。
擴(kuò)展--Cocoa Touch Static Library的打包
Cocoa Touch Static Library打包出來的是.a格式的靜態(tài)庫击儡,會(huì)把Link Binary With Libraries里面的靜態(tài)庫一起打包到.a靜態(tài)庫中,測試工程點(diǎn)我蝠引。
如何打包一個(gè)靜態(tài)庫阳谍,但是不包含其中的依賴庫文件蛀柴?
引入依賴庫頭文件即可,因?yàn)殪o態(tài)庫只編譯不鏈接边坤。(但是如果Cocoa Touch Static Library 里面填入了第三方的靜態(tài)庫名扛,會(huì)自動(dòng)打包)
.a和.framework都是靜態(tài)庫格式,只是.framework格式包括了靜態(tài)庫文件茧痒、頭文件、資源文件融蹂,故而更容易使用旺订。
如何直接使用.a靜態(tài)庫,不要靜態(tài)庫的頭文件超燃?
Link Binary With Libraries中添加.a靜態(tài)庫区拳;
在使用靜態(tài)庫的函數(shù)前添加聲明,但是不定義實(shí)現(xiàn)意乓;
這樣編譯時(shí)樱调,會(huì)根據(jù)聲明在全局查找定義;