20211229動態(tài)庫與靜態(tài)庫

兩種靜態(tài)庫:.a和.framework 的區(qū)別

庫是.o文件的合集
常見的庫文件格式:
.a: 常見的靜態(tài)庫
.dylib: 動態(tài)庫
.framework: framework實際上是一種打包方式,將庫的二進制文件、頭文件、有關(guān)的資源文件打包在一起。有靜態(tài)庫和動態(tài)庫2種:靜態(tài)庫framework = header+.a+簽名+資源文件, 動態(tài)庫=header + .dylib + 簽名 + 資源文件。
.xcframework: 2018年推出的,針對不同架構(gòu)的制作剝離不同的庫文件

庫(L ibrary):-段編譯好的二進制代碼,加上頭文件就可以供別人使用。
庫的是使用場景:
1.某些代碼需要給別人使用,但是我們不希望別人看到源碼,就需要以,庫的形式進行封裝,只暴露出頭文件。
2.對于某些不會進行大的改動的代碼,我們]想減少編譯的時間,就可以把它打包成庫,因為庫是已經(jīng)編譯好的二進制了,編譯的時候只需要Link一下,不會浪費編譯時間。

如何查看一個庫是靜態(tài)庫還是動態(tài)庫
cd到需查看的庫文件目錄里,使用命令: file + 庫名稱, 如下得出AFNetworking是一個靜態(tài)庫

Macintosh:靜態(tài)庫原理 johnson$ cd /Users/johnson/Downloads/靜態(tài)庫/上課代碼/鏈接 靜態(tài)庫AFNetworking/AFNetworking
Macintosh:AFNetworking johnson$ file libAFNetworking.a
libAFNetworking.a: current ar archive

將test.m文件生成test.o目標文件

Macintosh:鏈接靜態(tài)庫AFNetworking johnson$ clang -x objective-c \
> -target x86_64-apple-macos10.14.6 \
> -fobjc-arc \
> -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk \
> -I./AFNetworking \
> -c test.m -o test.o
/**
    將test.m編譯成test.o:
    1. 使用OC
    2. 生成的是X86_64_macOS架構(gòu)的代碼
        Big Sur是:x86_64-apple-macos11.1,之前是:x86_64-apple-macos10.15
    3. 使用ARC
    4. 使用的SDK的路徑在:
        Big Sur是:/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk
        之前是:/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk
    5. 用到的其他庫的頭文件地址在./Frameworks/TestExample.framework/Headers
 */

查看庫里的頭文件列表
ar -t + 庫名稱

Macintosh:AFNetworking johnson$ ar -t libAFNetworking.a
__.SYMDEF
AFAutoPurgingImageCache.o
AFHTTPSessionManager.o
AFImageDownloader.o
AFNetworkActivityIndicatorManager.o
AFNetworking-dummy.o
AFNetworkReachabilityManager.o
AFSecurityPolicy.o
AFURLRequestSerialization.o
AFURLResponseSerialization.o
AFURLSessionManager.o
UIActivityIndicatorView+AFNetworking.o
UIButton+AFNetworking.o
UIImageView+AFNetworking.o
UIProgressView+AFNetworking.o
UIRefreshControl+AFNetworking.o
WKWebView+AFNetworking.o
使用clang命令生成.o目標文件

clang 是一個 C、C++ 和 Objective-C 編譯器,它包含 prepro- 處理、解析、優(yōu)化、代碼生成、匯編和鏈接。

clang命令參數(shù):

 -x: 指定編譯文件語言類型
 -g: 生成調(diào)試信息
 -c: 生成目標文件,只運行preprocess,compile,assemble,不鏈接
 -o: 輸出文件
 -isysroot: 使用的SDK路徑
 1. -I<directory> 在指定目錄尋找頭文件 header search path
 2. -L<dir> 指定庫文件路徑(.a\.dylib庫文件) library search path
 3. -l<library_name> 指定鏈接的庫文件名稱(.a\.dylib庫文件)other link flags -lAFNetworking
 -F<directory> 在指定目錄尋找framework framework search path
 -framework <framework_name> 指定鏈接的framework名稱 other link flags -framework AFNetworking

ar命令參數(shù):

`ar`壓縮目標文件,并對其進行編號和索引,形成靜態(tài)庫。同時也可以解壓縮靜態(tài)庫,查看有哪些目標文件:
ar -rc a.a a.o
-r: 像a.a添加or替換文件
-c: 不輸出任何信息
-t: 列出包含的目標文件

當前靜態(tài)庫原理文件夾下面有test.m文件, 及StaticLibrary文件夾里面的TestExample.h,TestExample.m文件

靜態(tài)庫原理.png

test.m,TestExample.h,TestExample.m文件內(nèi)容分別如下

#import <Foundation/Foundation.h>
//眼 mudule -> .h
// .h -> 二進制
#import "TestExample.h"
int main(){
    NSLog(@"testApp----");
    TestExample *manager = [TestExample new];
    [manager lg_test: nil];
    return 0;
}
#import <Foundation/Foundation.h>
@interface TestExample : NSObject
- (void)lg_test:(_Nullable id)e;
@end
#import "TestExample.h"
@implementation TestExample

- (void)lg_test:(_Nullable id)e {
    NSLog(@"TestExample----");
}
@end

test.o鏈接libTestExample.a生成test可執(zhí)行文件
①test.m 生成test.o目標文件, TestExample.m生成 TestExample.o目標文件

//生成test.o文件
clang -x objective-c \
-target x86_64-apple-macos10.14.6 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk \
-I./StaticLibrary \
-c test.m -o test.o

//生成TestExample.o文件
clang -x objective-c \
-target x86_64-apple-macos10.14.6 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk \
-c TestExample.m -o TestExample.o

/**
    test.o鏈接libTestExample.a生成test可執(zhí)行文件
    -L./StaticLibrary 在當前目錄的子目錄StaticLibrary查找需要的庫文件
    -lTestExample 鏈接的名稱為libTestExample/TestExample的動態(tài)庫或者靜態(tài)庫
    查找規(guī)則:先找lib+<library_name>的動態(tài)庫,找不到,再去找lib+<library_name>的靜態(tài)庫,還找不到,就報錯
 */

TestExample添加.dylib后綴,然后再刪除.dylib后綴, TestExample.dylib = TestExample

② test.o鏈接libTestExample.a生成test可執(zhí)行文件

clang -target x86_64-apple-macos10.14.6 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk \
-L./StaticLibrary \
-lTestExample \
test.o -o test
/**
    test.o鏈接libTestExample.a生成test可執(zhí)行文件
    -L./StaticLibrary 在當前目錄的子目錄StaticLibrary查找需要的庫文件
    -lTestExample 鏈接的名稱為libTestExample/TestExample的動態(tài)庫或者靜態(tài)庫
    查找規(guī)則:先找lib+<library_name>的動態(tài)庫,找不到,再去找lib+<library_name>的靜態(tài)庫,還找不到,就報錯
    -syslibroot: 系統(tǒng)庫文件的目錄
 */

在終端執(zhí)行test可執(zhí)行文件,查看鏈接是否成功 **
在終端執(zhí)行
lldb**命令進入lldb模式下,在執(zhí)行 file test 查看文件類型, 然后執(zhí)行 r 命令運行test可執(zhí)行文件, 輸入內(nèi)容文件如下,

Macintosh:靜態(tài)庫原理 johnson$ lldb
(lldb) file test
Current executable set to 'test' (x86_64).
(lldb) r
Process 3972 launched: '/Users/johnson/Downloads/靜態(tài)庫/上課代碼/靜態(tài)庫原理/test' (x86_64)
2021-12-30 14:51:42.718508+0800 test[3972:1076896] testApp----
2021-12-30 14:51:42.718756+0800 test[3972:1076896] TestExample----
Process 3972 exited with status = 0 (0x00000000)

制作靜態(tài)庫

.a靜態(tài)庫

ar -rc 將.o 文件打包為.a 文件

Macintosh:StaticLibrary johnson$ ar -rc libTestExample.a TestExample.o
/**
 `ar`壓縮目標文件,并對其進行編號和索引,形成靜態(tài)庫。同時也可以解壓縮靜態(tài)庫,查看有哪些目標文件:
 ar -rc a.a a.o
    -r: 像a.a添加or替換文件
    -c: 不輸出任何信息
    -t: 列出包含的目標文件
 */
.framework靜態(tài)庫

編譯好的二進制文件,Headers放入以.framwork結(jié)尾的文件夾中


手動制作framework.png

test.o 鏈接TestExample.framework生成test可執(zhí)行文件

//生成test.o目標文件, -I./Frameworks/TestExample.framework/Headers \ 指定需要使用的TestExample.h目錄
clang -target x86_64-apple-macos10.14.6 \
> -fobjc-arc \
> -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk \
> -I./Frameworks/TestExample.framework/Headers \
> -c test.m -o test.o

//生成test二進制執(zhí)行文件 
clang -target x86_64-apple-macos10.14.6 \
> -fobjc-arc \
> -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk \
> -F./Frameworks \
> -framework TestExample \
> test.o -o test

//在lldb模式下驗證是否成
Macintosh:靜態(tài)庫與Framework johnson$ lldb
(lldb) file test
Current executable set to 'test' (x86_64).
(lldb) r
Process 6190 launched: '/Users/johnson/Downloads/靜態(tài)庫/上課代碼/靜態(tài)庫與Framework/test' (x86_64)
2021-12-30 20:07:56.742017+0800 test[6190:1352737] testApp----
Process 6190 exited with status = 0 (0x00000000)

shell腳本
image.png

build.sh文件內(nèi)容如下:

#定義變量SYSROOT、FILE_NAME、HEADER_SEARCH_PATH。 使用時直接:$變量名, 或者${變量名},{}外還可以拼接字符
SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk
FILE_NAME=test
HEADER_SEARCH_PATH=./StaticLibrary

echo "-----開始編譯test.m"
#一行shell腳本多個參數(shù)使用'\'來添加多個shell參數(shù), 沒有'\'表示一行命令結(jié)束
clang -x objective-c \
-target x86_64-apple-macos10.15 \
-fobjc-arc \
-isysroot $SYSROOT \
-I${HEADER_SEARCH_PATH} \
-c ${FILE_NAME}.m -o ${FILE_NAME}.o

echo "-----開始進入StaticLibrary"

#pushd進入到某個目錄下,須與popd一起使用, 等同于cd, pushd的是臨時, cd進入后就是新路勁無法返回
pushd ./StaticLibrary

clang -x objective-c \
-target x86_64-apple-macos10.15 \
-fobjc-arc \
-isysroot $SYSROOT \
-c TestExample.m -o TestExample.o

ar -rc libTestExample.a TestExample.o
echo "-----開始退出StaticLibrary"
popd

echo "-----開始test.o to test EXEC"
clang -target x86_64-apple-macos10.15 \
-fobjc-arc \
-isysroot $SYSROOT \
-L./StaticLibrary \
-lTestExample \
${FILE_NAME}.o -o ${FILE_NAME}

執(zhí)行build.sh shell文件, 在shell文件所在目錄下 ./shellfilename.sh即可。
如果提示zsh: permission denied: . fbuild. sh,沒有執(zhí)行權(quán)限, 使用chmod +x ./shellfilename.sh

chmod +x ./build.sh

執(zhí)行build.sh 成功輸出如下

Macintosh:靜態(tài)庫原理 johnson$ ./build.sh
-----開始編譯test.m
-----開始進入StaticLibrary
~/Downloads/靜態(tài)庫/上課代碼/靜態(tài)庫原理/StaticLibrary ~/Downloads/靜態(tài)庫/上課代碼/靜態(tài)庫原理
-----開始退出StaticLibrary
~/Downloads/靜態(tài)庫/上課代碼/靜態(tài)庫原理
-----開始test.o to test EXEC

pushd popd 與cd 區(qū)別
cd: 修改當前pwd目錄棧,修改了就無法返回
pushd: 新建一個臨時目錄棧
popd:使用popd直接返回之前的目錄棧

objdump 查看二進制文件里代碼的使用

objdump --macho -d test

#import <Foundation/Foundation.h>
//眼 mudule -> .h
// .h -> 二進制
#import "TestExample.h"
int main(){
    NSLog(@"testApp----");
//    TestExample *manager = [TestExample new];
//    [manager lg_test: nil];
    return 0;
}

執(zhí)行objdump --macho -d test 命令,輸出如下


image.png
#import <Foundation/Foundation.h>
//眼 mudule -> .h
// .h -> 二進制
#import "TestExample.h"
int main(){
    NSLog(@"testApp----");
    TestExample *manager = [TestExample new];
    [manager lg_test: nil];
    return 0;
}
image.png
dead strip 代碼剝離

使用workspace來聯(lián)調(diào)framework,將.app project 和 .framework project放在同一個workspace里進行調(diào)試,編譯APP同時也編譯framework。
創(chuàng)建xcworkspace: file -> seve as workspace


添加project到workspace.png

分類是在在運行時動態(tài)加載的, 所以在含有分類的庫文件在編譯時沒問題,但在運行時會找不到分類方法。比如下面的報錯:

2022-01-07 15:55:40.224374+0800 LGApp[35583:14201692] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[LGOneObject lg_test_category]: unrecognized selector sent to instance 0x600002834890'

通常我們在遇到這個問題時百度告訴我們的就是設(shè)置buildsetting -> other linker flags為 -Objc, -all_load
此時我們也可以通過配置xcconfig文件來解決:

// -all_load  全部加載,不剝離代碼
// -ObjC   只加載oc代碼,其他剝離
// -force_load :那些靜態(tài)庫 -》dead strip    這個庫不剝離
LGSTATIC_FRAMEWORK_PATH=${BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/LGStaticFramework.framework/LGStaticFramework
OTHER_LDFLAGS=-Xlinker -force_load $LGSTATIC_FRAMEWORK_PATH
// clang -Xlinker -all_load -> ld

dead strip :

dead strip 是鏈接器提供的優(yōu)化方式,移除沒有被入口點使用或者導出的代碼。
dead strip 主要針對靜態(tài)庫鏈接過程中來進行四代碼剝離,有4種方式: -noall_load、 -all_load 、-ObjeC 、-force_load <file>, 靜態(tài)庫中默認使用的是-noall_load進行編碼。
使用 man ld 查看

man ld
然后 /dead_strip 得到如下
-dead_strip
                 Remove functions and data that are unreachable by the entry point or exported symbols.
// 移除沒有被入口點使用或者導出的代碼。

test.m文件內(nèi)容如下

#import <Foundation/Foundation.h>
//眼 mudule -> .h
// .h -> 二進制
//#import "TestExample.h"

void global_function(){
}

int main(){
    NSLog(@"testApp----");
//    global_function();
//    TestExample *manager = [TestExample new];
//    [manager lg_test: nil];
    return 0;
}

static void static_function(){
}

build.文件內(nèi)容如下

#test 二進制可執(zhí)行文件鏈接一個TestExample靜態(tài)庫

#定義變量SYSROOT、FILE_NAME、HEADER_SEARCH_PATH。 使用時直接:$變量名, 或者${變量名}
SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk
FILE_NAME=test
HEADER_SEARCH_PATH=./StaticLibrary

echo "-----開始編譯test.m"
#一行shell腳本多個參數(shù)使用'\'來添加多個shell參數(shù), 沒有'\'表示一行命令結(jié)束
: '
①將test.m 文件編譯為test.o 文件
1、使用-x 指定編譯文件語言類型為OC
2、生成的是X86_64_macOS架構(gòu)的代碼
3、使用ARC
4、isysroot 指定使用SDK的路徑
5、-I 在指定目錄尋找頭文件 header search path
6、-c 生成目標頭文件 -o 輸出文件
'
clang -x objective-c \
-target x86_64-apple-macos10.15 \
-fobjc-arc \
-isysroot $SYSROOT \
-I${HEADER_SEARCH_PATH} \
-c ${FILE_NAME}.m -o ${FILE_NAME}.o

echo "-----開始進入StaticLibrary"
#pushd進入到某個目錄下,須與popd一起使用, 等同于cd, pushd的是臨時, cd進入后就是新路勁無法返回
: '
② 進入到./StaticLibrary文件目錄下將TestExample.m 文件編譯為TestExample.o文件, 然后將TestExample.o文件壓縮為TestExample.a 靜態(tài)庫
1、使用-x 指定編譯文件語言類型為OC
2、生成的是X86_64_macOS架構(gòu)的代碼
3、使用ARC
4、isysroot 指定使用SDK的路徑
5、-c 生成目標頭文件 -o 輸出文件
6、使用ar -rc 將.o文件壓縮為.a 靜態(tài)庫
'
pushd ./StaticLibrary

clang -x objective-c \
-target x86_64-apple-macos10.15 \
-fobjc-arc \
-isysroot $SYSROOT \
-c TestExample.m -o TestExample.o

ar -rc libTestExample.a TestExample.o
echo "-----開始退出StaticLibrary"
popd



<<'COMMENT'
③使用test.o文件生成一個鏈接TestExample靜態(tài)庫的test二進制可執(zhí)行文件
1、生成x86_64_macOS架構(gòu)代碼
2、使用arc
3、使用SDK的路勁
4、靜態(tài)庫鏈接方式
5、指定鏈接庫所在位置
6、指定鏈接庫的名稱
7、.o生成生成macho二進制可執(zhí)行文件
通過-L 指定所鏈接靜態(tài)的的位置
通過-l 指定所鏈接靜態(tài)庫名稱
通過test.o 得到一個鏈接TestExample靜態(tài)庫的test可執(zhí)行文件
COMMENT
echo "-----開始test.o to test EXEC"
clang -target x86_64-apple-macos10.15 \
-fobjc-arc \
-isysroot $SYSROOT \
-Xlinker -dead_strip \
-L./StaticLibrary \
-lTestExample \
${FILE_NAME}.o -o ${FILE_NAME}

靜態(tài)庫編譯時是默認不鏈接的,當?shù)冖鄄矫顑?nèi)容如下時,

clang -target x86_64-apple-macos10.15 \
-fobjc-arc \
-isysroot $SYSROOT \
-L./StaticLibrary \
-lTestExample \
${FILE_NAME}.o -o ${FILE_NAME}
默認不鏈接動態(tài)庫.png

當?shù)冖鄄矫顑?nèi)容如下, 鏈接內(nèi)容為-all_load,添加參數(shù) -Xlinker -all_load

clang -target x86_64-apple-macos10.15 \
-fobjc-arc \
-isysroot $SYSROOT \
-Xlinker -all_load \
-L./StaticLibrary \
-lTestExample \
${FILE_NAME}.o -o ${FILE_NAME}
-all_load.png

當?shù)谌糠置顑?nèi)容如下, 鏈接參數(shù)為dead strip,-Xlinker -dead_strip,

clang -target x86_64-apple-macos10.15 \
-fobjc-arc \
-isysroot $SYSROOT \
-Xlinker -dead_strip \
-L./StaticLibrary \
-lTestExample \
${FILE_NAME}.o -o ${FILE_NAME}

dead strip:沒有被導出符號使用的代碼就會被移除


不使用函數(shù)global_function().png

比如:global_function();這個函數(shù)沒有被調(diào)動,如果打開注釋,調(diào)用此函數(shù), 那么結(jié)果如下
使用函數(shù)global_function().png

函數(shù)符號被什么調(diào)用

clang -target x86_64-apple-macos10.15 \
-fobjc-arc \
-isysroot $SYSROOT \
-Xlinker -dead_strip \
-Xlinker -all_load \
-Xlinker -why_live -Xlinker global_function \
-L./StaticLibrary \
-lTestExample \
${FILE_NAME}.o -o ${FILE_NAME}
?著作權(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)容