歡迎加入QQ討論群258996829
麥子學(xué)院 頭像
蘋果6袋
6
麥子學(xué)院

關(guān)于靜態(tài)庫動態(tài)庫的一些思考

發(fā)布時間:2016-06-20 15:12  回復(fù):0  查看:3786   最后回復(fù):2016-06-20 15:12  

最近整理一些項目以前依賴留下的問題,在使用 CocoaPods 和 Carthage 的時候引出了關(guān)于靜態(tài)庫和動態(tài)庫的思考,手動編譯靜態(tài)庫的朋友應(yīng)該知道,如果自己編譯了一個靜態(tài)庫,這個靜態(tài)庫依賴了 iOS 自帶的庫,即使你在 Xcode開發(fā)工具中顯式指定了依賴,當你在另一個工程中使用這個靜態(tài)庫的時候,依舊需要顯式在那個工程指定這個靜態(tài)庫所依賴的系統(tǒng)庫。

 

舉個栗子

AFNetworking 是個好東西,大家都喜歡用,在日常使用的時候,一般都是使用 CocoaPods 引入這個庫,筆者前面有篇文章分析了 CocoaPods 做了什么工作,但是卻沒有分析 CocoaPods 為何如此設(shè)計,這里先賣個關(guān)子不講。AFNetworking 依賴Security.framework MobileCoreServices.frameworkSystemConfiguration.framework 三個系統(tǒng)框架,如果我們將其編譯為靜態(tài)庫,然后這個靜態(tài)庫被其他工程依賴,那么其他工程依舊需要引入這三個框架。如果我們將其編譯為動態(tài)庫,則不需要依賴這三個框架。這個情況引起了筆者的興趣

 

分析

既然問題是編譯鏈接,那么就需要從動態(tài)庫和靜態(tài)庫入手分析,首先,我們需要明確的是,系統(tǒng)提供的 framework 都是動態(tài)庫的形式提供的。這很好理解,因為 UIKit 這類 framework 被使用的太頻繁了,內(nèi)存中完全只需要保留一份副本。這樣也能減輕 App 的大小。那么直接來著手模擬一下其編譯過程。

模擬靜態(tài)庫的編譯

創(chuàng)建一個動態(tài)庫用于模擬系統(tǒng)庫

dynamic.h

----------void hello();

 

dynamic.c

----------#include "dynamic.h"#include <stdio.h>

void hello() {

    printf("Hello World");

}

然后編譯打包動態(tài)庫

> gcc -c -o dynamic.o dynamic.c # 編譯為對象文件

> gcc -shared dynamic.o -o libdynamic.so

創(chuàng)建一個靜態(tài)庫用于模擬第三方靜態(tài)庫

static.h

--------void sayHello();

static.c

--------#include "static.h"#include "dynamic.h"

void sayHello() {

    hello();

}

然后編譯靜態(tài)庫

> gcc -c -o static.o static.c

> ar -r libstatic.a static.o

這里大家有沒有發(fā)現(xiàn)一個問題,靜態(tài)庫實際上并沒有鏈接動態(tài)庫,僅僅只是 include 了一個頭文件用于編譯通過。最終生成的靜態(tài)庫根本不知道動態(tài)庫的存在。然后創(chuàng)建一個帶有main 函數(shù)的程序

hello.c

-------#include "static.h"int main(int argc, char *argv[]) {

    sayHello();

}

然后編譯

> gcc -o hello hello.c -L. -lstatic# 直接報錯沒有找到 hello() 的二進制代碼

Undefined symbols for architecture x86_64:

  "_hello", referenced from:

      _sayHello in libstatic.a(static.o)

ld: symbol(s) not found for architecture x86_64

clang: error: linker command failed with exit code 1 (use -v to see invocation)

模擬動態(tài)庫的編譯

創(chuàng)建一個動態(tài)庫用于模擬系統(tǒng)庫

dynamic1.h

----------void hello();

 

dynamic1.c

----------#include "dynamic1.h"#include <stdio.h>

void hello() {

    printf("Hello World");

}

然后編譯打包動態(tài)庫

> gcc -c -o dynamic1.o dynamic1.c # 編譯為對象文件

> gcc -shared dynamic1.o -o libdynamic1.so

創(chuàng)建動態(tài)庫用于模擬第三方動態(tài)庫

dynamic2.h

--------void sayHello();

 

dynamic2.c

--------#include "dynamic2.h"#include "dynamic1.h"

void sayHello() {

    hello();

}

然后編譯打包動態(tài)庫

> gcc -c -o dynamic2.o dynamic2.c # 編譯為對象文件

> gcc -shared dynamic2.o -o libdynamic2.so# 報錯,顯示 dynamic2.o 中沒有 hello() 二進制代碼

Undefined symbols for architecture x86_64:

  "_hello", referenced from:

      _sayHello in dynamic2.o

ld: symbol(s) not found for architecture x86_64

clang: error: linker command failed with exit code 1 (use -v to see invocation)

> gcc -shared dynamic2.o -o libdynamic2.so -L. -ldynamic1# 成功,無報錯

然后創(chuàng)建一個帶有 main 函數(shù)的程序

hello.c

-------#include "static.h"int main(int argc, char *argv[]) {

    sayHello();

}

然后編譯

> gcc -o hello hello.c -L. -ldynamic2

 

總結(jié)

從上面兩個編譯過程大家應(yīng)該也能明白了,靜態(tài)庫實際上只是對象文件的打包,也就是說,只經(jīng)過了編譯過程,而沒有鏈接過程,編譯一個靜態(tài)庫甚至只需要滿足所有函數(shù)的聲明就行,而動態(tài)庫雖然沒有經(jīng)過正規(guī)的鏈接,但是實際上還是通過 gcc 做了跟其他動態(tài)庫的鏈接,動態(tài)庫自身有了依賴的概念。所以不需要在工程中顯式依賴了。這里可能有朋友想要問,這種知識有什么用處?實際上這種知識在依賴管理中有用,前面說過 CocoaPods 對工程修改了很多內(nèi)容,這是有原因的,因為 iOS8 之前是不存在動態(tài)庫的,只存在靜態(tài)庫,CocoaPods 不得不對目標工程也作出修改來添加對其他庫的依賴,因為靜態(tài)庫不知道自身依賴什么庫。而 Carthage 則只支持 iOS8 和動態(tài)庫,所以完全可以沒有侵入性,只需要提供一個動態(tài)庫,然后工程依賴這個動態(tài)庫就行了。

 

原文來自:推酷

您還未登錄,請先登錄

熱門帖子

最新帖子

?