前言
在上文《編譯與鏈接過程的思考》評論中暴走大牙提到了靜態(tài)庫和動態(tài)庫依賴的問題,還在群里提了幾個測試樣例和測試工程。
大致介紹下測試工程和如何進行測試:
工程P為主工程,其中有4個子工程A、B、C、D,子工程打包的庫為動態(tài)庫或靜態(tài)庫,子工程之間存在依賴關(guān)系。
通過修改主工程的依賴庫,以及子工程的依賴關(guān)系以及打包類型,測試動態(tài)庫依賴靜態(tài)庫、靜態(tài)庫依賴動態(tài)庫、靜態(tài)庫依賴靜態(tài)庫的情況。
正文
在測試之前,先簡單說明下靜態(tài)庫和動態(tài)庫的打包方式
- Cocoa Touch Framework
- Mach-O Type 為 Static
打包的.framework文件為靜態(tài)庫 - Mach-O Type 為 Dynamic
打包的.framework文件為動態(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)用順序為B->A,正常;
- 對于TestB,我們引入C的頭文件,然后調(diào)用C的foo,打印的調(diào)用順序是A,異常;
結(jié)果思考??
靜態(tài)庫的生成只有編譯,沒有鏈接;
當(dāng)工程同時存在庫A和C時,兩個foo的函數(shù)符號在鏈接的時候,先引入者優(yōu)先;
驗證:把工程依賴順序從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)庫依賴動態(tài)庫
測試環(huán)境
庫A、B、C、D均采用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();
-
靜態(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 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)用動態(tài)庫A的函數(shù),正常;
- 對于第二組測試,我們調(diào)用靜態(tài)庫D的函數(shù)call_foo_d,在函數(shù)call_foo_d中調(diào)用動態(tài)庫A的函數(shù),異常;(預(yù)想中是調(diào)用動態(tài)庫C的函數(shù))
結(jié)果思考??
靜態(tài)庫的生成只有編譯,沒有鏈接;
那么在靜態(tài)庫D生成的過程中,只是確定了靜態(tài)庫D需要用到動態(tài)庫中的foo函數(shù);
當(dāng)運行時,加載了動態(tài)庫A、C,其中兩個庫均含有foo函數(shù);動態(tài)鏈接器,按照加載的順序,取到動態(tài)庫A中的foo函數(shù);
所以靜態(tài)庫B、D調(diào)用的foo函數(shù)均是動態(tài)庫A中的foo函數(shù)。
驗證:我們調(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.
動態(tài)庫依賴靜態(tài)庫
測試環(huán)境
庫A、B、C、D均采用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();
-
動態(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)用動態(tài)庫B的函數(shù)call_foo_b,在函數(shù)call_foo_b中調(diào)用靜態(tài)庫A的函數(shù),正常;
- 對于第二組測試,我們調(diào)用動態(tài)庫D的函數(shù)call_foo_d,在函數(shù)call_foo_d中調(diào)用靜態(tài)庫C的函數(shù),正常;
結(jié)果思考??
工程依賴?yán)锩嬷挥袆討B(tài)庫B、D,沒有靜態(tài)庫A、C;
靜態(tài)庫A、C同名函數(shù)foo沒有沖突;
這兩個現(xiàn)象是原因是動態(tài)庫在生成的過程中,除了編譯還有鏈接的過程。如果動態(tài)庫依賴靜態(tài)庫,在生成動態(tài)庫時會將靜態(tài)庫的代碼合并到動態(tài)庫中。
擴展
如果動態(tài)庫B、D的函數(shù)名字使用一樣的call_foo,調(diào)用順序和Link Binary With Libraries相關(guān),與embeded的順序無關(guān);(embeded只是把動態(tài)庫放入bundle中,關(guān)鍵在于鏈接器的順序)
動態(tài)庫依賴動態(tài)庫
測試環(huán)境
動態(tài)庫A、B、C、D均采用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();
-
動態(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)用動態(tài)庫B的函數(shù)call_foo_b,在函數(shù)call_foo_b中調(diào)用動態(tài)庫A的foo函數(shù),正常;
- 對于第二組測試,我們調(diào)用動態(tài)庫D的函數(shù)call_foo_d,在函數(shù)call_foo_d中調(diào)用動態(tài)庫C的foo函數(shù),正常;
結(jié)果思考??
四個動態(tài)庫都需要Link和Embeded;
與靜態(tài)庫依賴動態(tài)庫的測試樣例不同,這次雖然動態(tài)庫A、C存在同名函數(shù)foo,但是調(diào)用的時候沒有沖突。
動態(tài)庫依賴動態(tài)庫,在生成動態(tài)庫的時候不會把依賴的動態(tài)庫合并到動態(tài)庫中。
總結(jié)
靜態(tài)庫的生成只有編譯,沒有鏈接;
動態(tài)庫的生成除了編譯還有鏈接的過程;
如果動態(tài)庫依賴靜態(tài)庫,在生成動態(tài)庫時會將靜態(tài)庫的代碼合并到動態(tài)庫中;
- 靜態(tài)庫A依賴靜態(tài)庫B,使用時需要在Link Binary With Libraries引入靜態(tài)庫A、B;
- 靜態(tài)庫A依賴動態(tài)庫B,使用時需要在Link Binary With Libraries引入靜態(tài)庫A和動態(tài)庫B,并且在Embeded Binaries引入動態(tài)庫B;
- 動態(tài)庫A依賴靜態(tài)庫B,使用時需要在Link Binary With Libraries引入動態(tài)庫A,并且在Embeded Binaries引入動態(tài)庫A;
- 動態(tài)庫A依賴動態(tài)庫B,使用時需要在Link Binary With Libraries引入動態(tài)庫A和B,并且在Embeded Binaries引入動態(tài)庫A和B;
所有的代碼都可以在這里找到。
擴展--Cocoa Touch Static Library的打包
Cocoa Touch Static Library打包出來的是.a格式的靜態(tài)庫,會把Link Binary With Libraries里面的靜態(tài)庫一起打包到.a靜態(tài)庫中,測試工程點我。
如何打包一個靜態(tài)庫,但是不包含其中的依賴庫文件?
引入依賴庫頭文件即可,因為靜態(tài)庫只編譯不鏈接。(但是如果Cocoa Touch Static Library 里面填入了第三方的靜態(tài)庫,會自動打包)
.a和.framework都是靜態(tài)庫格式,只是.framework格式包括了靜態(tài)庫文件、頭文件、資源文件,故而更容易使用。
如何直接使用.a靜態(tài)庫,不要靜態(tài)庫的頭文件?
Link Binary With Libraries中添加.a靜態(tài)庫;
在使用靜態(tài)庫的函數(shù)前添加聲明,但是不定義實現(xiàn);
這樣編譯時,會根據(jù)聲明在全局查找定義;



