靜態(tài)庫與動態(tài)庫的思考

前言

在上文《編譯與鏈接過程的思考》評論中暴走大牙提到了靜態(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ù)聲明在全局查找定義;

最后編輯于
?著作權(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)容