目錄
一、可執行文件鏈接動態庫.dylib
準備代碼如下:
test.m
文件中代碼如下:
#import <Foundation/Foundation.h>
#import <AFNetworking.h>
int main(){
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
NSLog(@"testApp----%@", manager);
return 0;
}
參照上篇文章編譯鏈接動態庫:
1、生成目標文件
使用clang
命令編譯main.m
代碼
cd main.m 文件目錄下
clang -x objective-c \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \
-I./AFNetworking \
-c test.m -o test.o
2、鏈接靜態庫生成可執行文件
使用clang
命令鏈接動態庫命令如下:
clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \
-L./AFNetworking \
-lAFNetworking \
test.o -o test
3、在終端執行test
可執行文件
這里就出現鏈接動態庫和鏈接靜態不同的地方。
二、dyld加載動態庫流程
從上面這張流程圖中我們可以看到dyld
從Mach-O
中讀取LC_LOAD_DYLIB
保存動態庫信息來加載動態庫,因此上面鏈接AFNetworking
動態庫的test
可執行文件中沒有找到動態庫的路徑所以運行時報了錯。
1、查看Mach-O
中LC_LOAD_DYLIB
信息
// -A:向下尋找 -B:向上尋找 5:5行
otool -l test | grep 'DYLIB' -A 5
由此可以看出動態庫的路徑由兩部分組成,一部分是
@rpath
可執行文件提供,另一部分是動態庫中提供
2、@rpath
Runpath search Paths:dyld搜索路徑,誰需要鏈接動態庫誰就需要提供@rpath
運行時@rpath
指示dyld
按順序搜索路徑列表,以找到動態庫。@rpath
保存一個或 多個路徑的變量
因此可以分析出
test
可執行文件沒有提供@rpath
,導致動態庫的路徑不完整。
查看Mach-O
中@rpath
信息
可以看到此Mach-O
中的確沒有@rpath
3、Mach-O中添加@rpath
install_name_tool -add_rpath /Users/ztkj/Desktop/鏈接動態庫AFN test
4、修改動態庫中的路徑
由于AFNetworking
生成動態庫時的路徑和我們現在的文件結構不一致,所以還需要修改動態庫中的路徑
這里和前面查看Mach-O
中LC_LOAD_DYLIB
信息是一致的,修改路徑(name
-->參數使用-id
)代碼如下:
install_name_tool -id @rpath/AFNetworking libAFNetworking.dylib
重新鏈接動態庫生成可執行文件test
(執行第三步添加@rpath
)后即可運行
Xcode如果引入了第三方動態庫,那么在Build Settings中也會自動加上
install name
和rpath
三、創建動態庫.dylib
準備如下代碼:
build.sh
中的代碼如下:
echo "編譯test.m --- test.o"
clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \
-I./dylib \
-c test.m -o test.o
pushd ./dylib
echo "編譯TestExample.m --- TestExample.o"
clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \
-c TestExample.m -o TestExample.o
echo "編譯TestExample.o --- libTestExample.a"
# Xcode提供的工具生成靜態庫
libtool -static -arch_only x86_64 TestExample.o -o libTestExample.a
echo "編譯TestExample.a --- libTestExample.dylib"
# 通過.o生成動態庫
#clang -dynamiclib \
#-target x86_64-apple-macos11.1 \
#-fobjc-arc \
#-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \
#-Xlinker -install_name -Xlinker @rpath/TestExample \
#TestExample.o -o libTestExample.dylib
# dylib 最終鏈接產物 -》
ld -dylib -arch x86_64 \
-macosx_version_min 11.1 \
-syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \
-lsystem -framework Foundation \
-Xlinker -install_name -Xlinker @rpath/TestExample \
-all_load \
libTestExample.a -o libTestExample.dylib
popd
echo "鏈接libTestExample.dylib -- test EXEC"
clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \
-Xlinker -rpath -Xlinker @executable_path/dylib \
-L./dylib \
-lTestExample \
test.o -o test
# 添加@rpath
#install_name_tool -add_rpath @executable_path/dylib test
echo "-------DYLIB---------"
otool -l test | grep 'DYLIB' -A 5
echo "-------RPATH---------"
otool -l test | grep 'RPATH' -A 5
其中為動態庫(install_name
)和可執行文件(rpath
)添加路徑的參數如下:
-Xlinker -install_name -Xlinker @rpath/TestExample \
//添加@rpath
-Xlinker -rpath -Xlinker @executable_path/dylib \
或install_name_tool -add_rpath @executable_path/dylib test
四、創建動態庫Framework
準備的目錄結構如下:
第一個build.sh
文件代碼如下:
clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \
-I./Frameworks/TestExample.framework/Headers \
-c test.m -o test.o
clang \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \
-F./Frameworks \
-framework TestExample \
test.o -o test
install_name_tool -add_rpath @executable_path/Frameworks test
echo "-------DYLIB---------"
otool -l test | grep 'DYLIB' -A 5
echo "-------RPATH---------"
otool -l test | grep 'RPATH' -A 5
第二個build.sh
文件代碼如下:
clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \
-I./Headers \
-c TestExample.m -o TestExample.o
#需要再鏈接動態庫的頭文件
#-I./Frameworks/TestExampleLog.framework/Headers \
clang -dynamiclib \
-target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \
-Xlinker -install_name -Xlinker @rpath/TestExample.framework/TestExample \
-F./Frameworks \
TestExample.o -o TestExample
#需要再鏈接動態庫的庫名
#-framework TestExampleLog \
#如果這個動態庫中需要再鏈接動態庫,就需要在這里為鏈接的動態庫提供rpath
#install_name_tool -add_rpath @loader_path/Frameworks TestExample
echo "-------DYLIB---------"
otool -l TestExample | grep 'DYLIB' -A 5
echo "-------ID---------"
otool -l TestExample | grep 'ID' -A 5
echo "-------RPATH---------"
otool -l TestExample | grep 'RPATH' -A 5
-
@executable_path
: 表示可執行程序所在的目錄,解析為可主程序執行文件的絕對路徑。 -
@loader_path
: 表示被加載的Mach-O
所在的目錄,用于動態庫鏈接其他動態庫時提供的rpath
路徑。
先執行第二個build.sh
文件再執行第一個build.sh
文件即可生成動態庫Framework
。此外在第二個build.sh
文件中還可以添加動態庫鏈接動態庫的參數。
五、tdb格式
什么是tdb格式?
tbd
全稱是text based stub libraries
, 本質上就是一個YAML
描述的文本文件。他的作用是用于記錄動態庫的一些信息,包括導出的符號、動態庫的架構信息、動態庫的依賴信息。用于避免在真機開發過程中直接使用傳統的dylib
。
對于真機來說,由于動態庫都是在設備上,在Xcode
上使用基于tbd
格式的偽framework
可以大大減少Xcode
的大小。
Xcode
編譯時通過讀取動態庫的tbd
即可完成編譯,只有運行時才會在執行動態庫中的代碼。
六、靜態庫與動態庫的區別
靜態庫:鏈接時會被完整的復制到可執行文件中,被多次使用就有多份拷貝。
動態庫:鏈接時不復制,程序運行時由系統動態加載到內存,系統只加載一次,多個程序共用(如系統的UIKit.framework等),節省內存。
總結
- 靜態庫可以鏈接變成動態庫,動態庫是最終的編譯產物,因此動態庫不能合并。
- 上架動態庫需要簽名,過多的動態庫會影響啟動速度
- SDK提供商一般選擇動態庫,自己開發中最好使用靜態庫