常見庫文件格式:.a
,.dylib
,.framework
,.xcframework
,.tdb
什么是庫(Library)?
庫(Library
)本質(zhì)上就是一段編譯好的二進制代碼,加上頭文件就可以供別人使用。
應用場景?
- 某些代碼需要給別人使用,但是不希望別人看到源碼,就需要以庫的形式進行封裝,只暴露出頭文件。
- 對于某些不會進行大的改動的代碼,我們想減少編譯的時間,就可以把它打包成庫,因為庫是已經(jīng)編譯好的二進制了,編譯的時候只需要
Link
一下,不會浪費編譯時間。
什么是鏈接(Link)?
庫在使用的時候需要鏈接(Link
),鏈接 的方式有兩種:
- 靜態(tài)
- 動態(tài)
靜態(tài)庫
靜態(tài)庫即靜態(tài)鏈接庫:可以簡單的看成一組目標文件的集合,即很多目標文件經(jīng)過壓縮打包后形成的文件。Windows
下的 .lib
,Linux
和 Mac
下的 .a
。Mac
獨有的.framework
。
缺點: 浪費內(nèi)存和磁盤空間,模塊更新困難。
靜態(tài)庫鏈接
將一份AFNetworking
靜態(tài)庫文件(.h
頭文件和.a
組成)和test.m
放到統(tǒng)一目錄。test.m
如下:
#import <Foundation/Foundation.h>
#import <AFNetworking.h>
int main(){
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
NSLog(@"test----%@", manager);
return 0;
}
直接終端查看下.a
靜態(tài)庫究竟是什么。
? AFNetworking file libAFNetworking.a
libAFNetworking.a: current ar archive
可以看到.a
實際上是一個文檔格式。也就是.o
文件的合集。可以通過ar
命令驗證下。
ar -- create and maintain library archives
? AFNetworking 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
確認.a
確實是.o
文件的合集。清楚了.a
后將AFNetworking
鏈接到test.m
文件。
1.通過clang
將test.m
編譯成目標文件.o
clang - the Clang C, C++, and Objective-C compiler
DESCRIPTION
clang is a C, C++, and Objective-C compiler which encompasses prepro-
cessing, parsing, optimization, code generation, assembly, and linking.
Depending on which high-level mode setting is passed, Clang will stop
before doing a full link. While Clang is highly integrated, it is
important to understand the stages of compilation, to understand how to
invoke it. These stages are:
Driver The clang executable is actually a small driver which controls
the overall execution of other tools such as the compiler,
assembler and linker. Typically you do not need to interact
with the driver, but you transparently use it to run the other
tools.
通過man
命令我們看到clang
是C
、C++
、OC
的編譯器
,是一個集合包含了預處理
、解析
、優(yōu)化
、代碼生成
、匯編化
、鏈接
。
clang -x objective-c \
-target x86_64-apple-ios14-simulator \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.2.sdk \
-I ./AFNetworking \
-c test.m -o test.o
回車后就生成了test.o
目標文件。
\
為了轉(zhuǎn)譯回車,讓命令換行更易讀。-x
制定編譯語言,-target
指定編譯平臺,-fobjc-arc
編譯成ARC
,-isysroot
指定用到的Foundation
的路徑,-I<directory>
在指定目錄尋找頭文件header search path
為什么生成目標文件只需要告訴頭文件的路徑就可以了?
因為在生成目標文件的時候,重定位符號表只需要記錄哪個地方的符號需要重定位。在連接的時候鏈接器會自動重定位。(上面的例子中只需要保留AFHTTPSessionManager
的符號。)
2..o
生成可執(zhí)行文件
clang -target x86_64-apple-ios14-simulator \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.2.sdk \
-L./AFNetworking \
-lAFNetworking \
test.o -o test
這個時候test
可執(zhí)行程序就生成了。
-L
要鏈接的庫文件(libAFNetworking.a
)目錄,-l
要鏈接的庫文件(libAFNetworking.a)這里只寫AFNetworking
是有查找規(guī)則的:先找lib+<library_name>的動態(tài)庫,找不到,再去找lib+<library_name>的靜態(tài)庫,還找不到,就報錯
。會自動去找libAFNetworking
。
經(jīng)過上面的編譯和鏈接清楚了其它參數(shù)都是固定的,那么鏈接成功一個庫文件有3個要素:
1. -I<directory>
在指定目錄尋找頭文件 header search path
(頭文件
)
2. -L<dir>
指定庫文件路徑(.a\.dylib
庫文件) library search path
(庫文件路徑
)
3. -l<library_name>
指定鏈接的庫文件名稱(.a\.dylib
庫文件)other link flags
-lAFNetworking (庫文件名稱
)
生成靜態(tài)庫
將自己的一個工程編譯成.a
靜態(tài)庫。工程只有一個文件HPExample``.h
和 .m
:
#import <Foundation/Foundation.h>
@interface HPExample : NSObject
- (void)hp_test:(_Nullable id)e;
@end
#import "HPExample.h"
@implementation HPExample
- (void)hp_test:(_Nullable id)e {
NSLog(@"hp_test----");
}
@end
將HPExample.m
編譯成.o
文件:
clang -x objective-c \
-target x86_64-apple-macos11.0 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-I./StaticLibrary \
-c HPExample.m -o HPExample.o
這個時候生成了HPExample.o
文件,由于工程只有一個.o
文件,直接將文件修改為libExample.dylib
或者libHPExample.a
。
然后創(chuàng)建一個test.m
文件調(diào)用HPExample
:
#import <Foundation/Foundation.h>
#import "HPExample.h"
int main(){
NSLog(@"testApp----");
HPExample *manager = [HPExample new];
[manager hp_test: nil];
return 0;
}
將test.m
編譯成test.o
:
clang -x objective-c \
-target x86_64-apple-macos11.0 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-I./StaticLibrary \
> -c test.m -o test.o
test.o
鏈接HPExample
:
clang -target x86_64-apple-macos11.0 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-L./StaticLibrary \
-lHPExample \
test.o -o test
現(xiàn)在就已經(jīng)生成了可執(zhí)行文件test
。
終端lldb
執(zhí)行test
:
? staticLibraryCreat lldb
(lldb) file test
Current executable set to '/Users/binxiao/projects/library/staticLibraryCreat/test' (x86_64).
(lldb) r
Process 2148 launched: '/Users/binxiao/projects/library/staticLibraryCreat/test' (x86_64)
2021-02-13 13:22:49.150091+0800 test[2148:13026772] testApp----
2021-02-13 13:22:49.150352+0800 test[2148:13026772] hp_test----
Process 2148 exited with status = 0 (0x00000000)
這也從側面印證了.a
就是.o
的合集。file test
是創(chuàng)建一個target
,r
是運行的意思。
接著再看下libHPExample.a
文件。
objdump --macho --private-header libHPExample.a
Mach header
magic cputype cpusubtype caps filetype ncmds sizeofcmds flags
MH_MAGIC_64 X86_64 ALL 0x00 OBJECT 4 1160 SUBSECTIONS_VIA_SYMBOLS
確認還是一個目標文件。
靜態(tài)庫的合并
根據(jù)上面的分析,那么靜態(tài)庫的合并也就是將所有的.o
放到一個文件中。
有兩個.a
庫:
靜態(tài)庫的合并有兩種方式:libAFNetworking.a
,libSDWebImage.a
1.ar -rc libAFNetworking.a libSDWebImage.a
ar -rc libAFNetworking.a libSDWebImage.a
就相當于將后面的libSDWebImage.a
合并到libAFNetworking.a
。
2.libtool -static -o <OUTPUT NAME> <LIBRARY_1> <LIBRARY_2>
libtool
合并靜態(tài)庫。
libtool -static \
-o \
libMerge.a \
libAFNetworking.a \
libSDWebImage.a
//libAFNetworking.a要為目標文件路徑,libMerge.a為輸出文件
這樣就合并了
libAFNetworking.a
和libSDWebImage.a
為libMerge.a
了。在這個過程中libtool
會先解壓兩個目標文件,然后合并。在合并的過程中有兩個問題:1.沖突問題。
2.
.h
文件。
clang
提供了mudule
可以預先把頭文件(.h
)預先編譯成二進制緩存到系統(tǒng)目錄中, 再去編譯.m
的時候就不需要再去編譯.h
了。
LC_LINKER_OPTION
鏈接器的特性,Auto-Link
。啟用這個特性后,當我們import <模塊>
,不需要我們再去往鏈接器去配置鏈接參數(shù)。比如import <framework>
我們在代碼里使用這個framework格式的庫文件,那么在生成目標文件時,會自動在目標文件的Mach-O
中,插入一個load command
格式是LC_LINKER_OPTION
,存儲這樣一個鏈接器參數(shù)-framework <framework>
。
動態(tài)庫
與靜態(tài)庫相反,動態(tài)庫在編譯時并不會被拷?到目標程序中,目標程序中只會存儲指向動態(tài)庫的引用。等到程序運行時,動態(tài)庫才會被真正加載進來。格式有:.framework
、.dylib
、.tdb
。
缺點:會導致一些性能損失。但是可以優(yōu)化,比如延遲綁定(Lazy Binding
)技術。
.tdb
tbd
全稱是text-based stub libraries
本質(zhì)上就是一個YAML
描述的文本文件。他的作用是用于記錄動態(tài)庫的一些信息,包括導出的符號、動態(tài)庫的架構信息、動態(tài)庫的依賴信息。用于避免在真機開發(fā)過程中直接使用傳統(tǒng)的dylib
。對于真機來說,由于動態(tài)庫都是在設備上,在Xcode
上使用基于tbd
格式的偽framework
可以大大減少Xcode
的大小。
framework
Mac OS/iOS
平臺還可以使用 Framework
。Framework
實際上是一種打包方式,將庫的二進制文件、頭文件和有關的資源文件打包到一起方便管理和分發(fā)。
Framework
和系統(tǒng)的 UIKit.Framework
還是有很大區(qū)別。系統(tǒng)的 Framework
不需要拷?到目標程序中,我們自己做出來的 Framework
哪怕是動態(tài)的,最后也還是要拷?到 App
中(App
和 Extension
的 Bundle
是共享的),因此蘋果又把這種 Framework
稱為 Embedded Framework
。
Embedded Framework
開發(fā)中使用的動態(tài)庫會被放入到ipa
下的framework
目錄下,基于沙盒運行。
不同的App
使用相同的動態(tài)庫,并不會只在系統(tǒng)中存在一份。而是會在多個App
中各自打包、簽名、加載一份。
framework
即可以代表動態(tài)庫也可以代表靜態(tài)庫。
生成framework
一般framework
格式:
接著HPExample
的例子構建一個framework
編譯
test.m
:
clang -x objective-c \
-target x86_64-apple-macos11.0 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-I ./Frameworks/HPExample.framework/Headers \
-c test.m -o test.o
鏈接.o
生成test
可執(zhí)行文件:
clang -target x86_64-apple-macos11.0 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-F./Frameworks \
-framework HPExample \
test.o -o test
執(zhí)行:
那么鏈接一個
framework
也就需要三個條件:1.
-I<directory>
:在指定目錄尋找頭文件 header search path
(頭文件)2.
-F<directory>
:在指定目錄尋找framework framework search path
3.
-framework <framework_name>
:指定鏈接的framework
名稱 other link flags -framework AFNetworking
腳本執(zhí)行命令
上面都是通過命令行來進行編譯連接的,每次輸入都很麻煩(即使粘貼復制),我們可以將命令保存在腳本中,通過執(zhí)行腳本來執(zhí)行命令。
還是以HPExample
為例,整理后腳本如下(可以加一些日志觀察執(zhí)行問題):
echo "test.m -> test.o"
clang -x objective-c \
-target x86_64-apple-macos11.0 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-I ./StaticLibrary \
-c test.m -o test.o
echo "pushd -> StaticLibrary"
#cd可以進入到一個目錄不推薦使用,cd會修改目錄棧上層,推薦使用 pushd,pushd是往目錄棧中push一個目錄。
pushd ./StaticLibrary
echo "HPExample.m -> HPExample.o"
clang -x objective-c \
-target x86_64-apple-macos11.0 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-I./StaticLibrary \
-c HPExample.m -o HPExample.o
echo "HPExample.o -> libHPExample.a"
#打包.o成靜態(tài)庫
ar -rc libHPExample.a HPExample.o
echo "popd -> StaticLibrary"
popd
echo "test.o -> test"
#鏈接
clang -target x86_64-apple-macos11.0 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk \
-L./StaticLibrary \
-lHPExample \
test.o -o test
執(zhí)行腳本(記的加可執(zhí)行權限x
):
這個時候就已經(jīng)自動編譯鏈接完成了,其中路徑是
pushd
和popd
自動生成的。可以簡單優(yōu)化下腳本:
SYSROOT=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.0.sdk
#${SYSROOT}和$SYSROOT都行,如果要匹配比如${SYSROOT}.mm則用{}
FILE_NAME=test
HEADER_SEARCH_PATH=./StaticLibrary
function MToOOrExec {
if [[ $2 == ".m" ]]; then
clang -x objective-c \
-target x86_64-apple-macos11.0 \
-fobjc-arc \
-isysroot ${SYSROOT} \
-I${HEADER_SEARCH_PATH} \
-c $1.m -o $1.o
else
clang -target x86_64-apple-macos11.0 \
-fobjc-arc \
-isysroot ${SYSROOT} \
-L${HEADER_SEARCH_PATH} \
-l$1 \
${FILE_NAME}.o -o ${FILE_NAME}
fi
return 0
}
echo "test.m -> test.o"
MToOOrExec ${FILE_NAME} ".m"
echo "pushd -> StaticLibrary"
#cd可以進入到一個目錄不推薦使用,cd會修改目錄棧上層,推薦使用 pushd,pushd是往目錄棧中push一個目錄。
pushd ${HEADER_SEARCH_PATH}
echo "HPExample.m -> HPExample.o"
MToOOrExec HPExample ".m"
echo "HPExample.o -> libHPExample.a"
#打包.o成靜態(tài)庫
ar -rc libHPExample.a HPExample.o
echo "popd -> StaticLibrary"
popd
echo "test.o -> test"
#鏈接
MToOOrExec HPExample ".o"
dead code strip
對于上面的例子,如果我們在test.m
中不使用HPExample
只是導入。
#import <Foundation/Foundation.h>
#import "HPExample.h"
int main(){
NSLog(@"test----");
// HPExample *manager = [HPExample new];
// [manager hp_test: nil];
return 0;
}
那么生成的test
執(zhí)行文件包含了HPExample
么?
objdump --macho --d test
看一下:
只有一個
main
和NSLog
。打開注釋的代碼在看下:
默認
clang
的dead code strip
是生效的。在有分類的情況下
看另外一個例子,我們直接用
Xcode
創(chuàng)建一個framework
,設置為靜態(tài)庫。(Targets -> Build Settings -> Linking -> Macho-type
)這個庫有一個
HPTestObject
以及HPTestObject+HPAdditions
。實現(xiàn)如下:HPTestObject
//.h
#import <Foundation/Foundation.h>
@interface HPTestObject : NSObject
- (void)hp_test;
@end
//.m
#import "HPTestObject.h"
#import "HPTestObject+HPAdditions.h"
@implementation HPTestObject
- (void)hp_test {
[self hp_test_additions];
}
@end
HPTestObject+HPAdditions
//.h
#import "HPTestObject.h"
@interface HPTestObject (HPAdditions)
- (void)hp_test_additions;
@end
//.m
#import "HPTestObject+HPAdditions.h"
@implementation HPTestObject (HPAdditions)
- (void)hp_test_additions {
NSLog(@"log: hp_test_additions");
}
@end
將HPTestObject
設置為public
:
我們知道分類是在運行時動態(tài)創(chuàng)建的,dead code strip
是在鏈接的過程中生效的。那么應該在鏈接的時候會strip
掉分類。
我們創(chuàng)建一個workspace驗證下
workspace
A. 可重用性。多個模塊可以在多個項目中使用。節(jié)約開發(fā)和維護時間。
B. 節(jié)省測試時間。單獨模塊意味著每個模塊中都可以添加測試功能。
C. 更好的理解模塊化思想。
1.File -> save as workspace
2.創(chuàng)建一個project(TestApp
)。
3.打開workspace
,添加一個project
(創(chuàng)建的TestApp
)(??需要關閉打開的文件才會出現(xiàn)Add Files to TestDeadCodeStrip
):
4.
ViewController.m
中使用HPTestObject
#import <HPStaticFramework/HPTestObject.h>
- (void)viewDidLoad {
[super viewDidLoad];
HPTestObject *hpObject = [HPTestObject new];
[hpObject hp_test];
}
5.運行
libc++abi.dylib: terminating with uncaught exception of type NSException
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[HPTestObject hp_test_additions]: unrecognized selector sent to instance 0x600001048020'
terminating with uncaught exception of type NSException
和預想的一樣直接報錯了,原因是dead code strip
脫掉了分類。要解決問題還是要告訴編譯器不要脫。
6.配置XCConfig
告訴編譯器不要脫。
//-Xlinker 告訴 clang -all_load 參數(shù)是傳給 ld 的。
OTHER_LDFLAGS=-Xlinker -all_load
再次運行App:
TestApp[8958:13347736] log: hp_test_additions
??
-Xlinker
告訴 clang
-all_load
參數(shù)是傳給ld
的。
-all_load
:全部鏈接
OTHER_LDFLAGS=-Xlinker -all_load
-ObjC
: OC
相關的代碼不要剝離
//OTHER_LDFLAGS=-Xlinker -ObjC
-force_load
:指定哪些靜態(tài)庫不要 dead strip
HPSTATIC_FRAMEWORK_PATH=${BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)/HPStaticFramework.framework/HPStaticFramework
OTHER_LDFLAGS=-Xlinker -force_load $HPSTATIC_FRAMEWORK_PATH
-noall_load
: 默認,沒有使用靜態(tài)庫代碼則不往可執(zhí)行文件添加。This is the default. This option is obsolete.
以上4種參數(shù)僅針對靜態(tài)庫。dead code strip
是在鏈接過程中連接器提供的優(yōu)化方式。
-dead_strip
Remove functions and data that are unreachable by the entry point or exported symbols.
移除沒有被入口點(也就是main
)和導出符號用到的代碼。
接著libraryDeadCodeStrip
工程驗證下:
修改test.m
如下:
#import <Foundation/Foundation.h>
//#import "HPExample.h"
//全局函數(shù)
void global_function() {
}
//entry point
int main(){
// global_function();
NSLog(@"test----");
// HPExample *manager = [HPExample new];
// [manager hp_test: nil];
return 0;
}
//本地
static void static_function(){
}
運行build.sh
objdump --macho --syms test
查看符號表:
可以看到?jīng)]有靜態(tài)庫
libHPExample.a
相關的代碼,加上all_load
再查看下:build.sh
修改增加
-Xlinker -all_load \
再次運行上面的步驟:
可以看到
hp_test
方法已經(jīng)有了。修改
-Xlinker -all_load
為-Xlinker -dead_strip
再查看下:global_function
和hp_test
都沒有了。打開
main
中global_function()
的注釋再看下:所以dead code strip
和-all_load
、-ObjC
、-force_load
、-noall_load
不是一個東西,他有一定規(guī)則:
- 入口點沒有使用->干掉
- 沒有被導出符號使用->干掉
接著-Xlinker -dead_strip
和-Xlinker -all_load
一起添加:
鏈接器有一個參數(shù)
-why_live
可以查看某一個符號為什么沒有被干掉,比如我們要知道global_function
為什么沒有被干掉:
-Xlinker -why_live -Xlinker _global_function
.o -> .o
與.o -> .a
.o -> .o
是合并成一個大的.o
再去鏈接生成可執(zhí)行文件。先組合再鏈接。所以這里dead code strip
干不掉,可以通過LTO
(Link-Time Optimization
)去優(yōu)化。
.o
鏈接靜態(tài)庫是.o
是去使用靜態(tài)庫。先dead code strip
再使用。
Embed
-
Do Not Embed
用于靜態(tài)庫 -
Embed & Sign
嵌入,用于動態(tài)庫,動態(tài)庫在運行時鏈接,所以它們編譯的時候需要被打進bundle
里面。靜態(tài)庫鏈接的時候代碼就已經(jīng)在一起了,所以不需要拷貝,直接Do Not Embed
就可以了。可以通過file
命令驗證:
file HPStaticFramework.framework/HPStaticFramework
HPStaticFramework.framework/HPStaticFramework: current ar archive random library
current ar archive
:說明是靜態(tài)庫,選擇Do not embed
Mach-0 dynamically
:說明是動態(tài)庫,選擇Embed
-
Embed Without Signing
Signing
:只用于動態(tài)庫,如果已經(jīng)有簽名了就不需要再簽名。終端執(zhí)行codesign -dv
判斷:
codesign -dv HPStaticFramework.framework
Executable=/Users/***/Library/Developer/Xcode/DerivedData/TestDeadCodeStrip-fhbiunbplvqefkftfystdixdxmkq/Build/Products/Debug-iphonesimulator/HPStaticFramework.framework/HPStaticFramework
Identifier=HotpotCat.HPStaticFramework
Format=bundle with generic
CodeDirectory v=20100 size=204 flags=0x2(adhoc) hashes=1+3 location=embedded
Signature=adhoc
Info.plist entries=20
TeamIdentifier=not set
Sealed Resources version=2 rules=10 files=2
Internal requirements count=0 size=12
Signature
:
code object is not signed at all
或者 adhoc
:選擇Embed and sign
其它
:表示已經(jīng)正確簽名,選擇Embed Without Signing
命令總結
clang命令參數(shù)
-x
: 指定編譯文件語言類型
-g
: 生成調(diào)試信息
-c
: 生成目標文件,只運行preprocess
,compile
,assemble
不鏈接
-o
: 輸出文件
-isysroot
: 使用的SDK路徑
-I<directory>
: 在指定目錄尋找頭文件 header search path
-L<directory>
:指定庫文件路徑(.a.dylib庫文件)library search path
-l<library_name>
: 指定鏈接的庫文件名稱(.a.dylib庫文件)other link flags -lAFNetworking
。鏈接的名稱為libAFNetworking
/AFNetworking
的動態(tài)庫或者靜態(tài)庫,查找規(guī)則:先找lib+<library_name>
的動態(tài)庫,找不到,再去找lib+<library_name>
的靜態(tài)庫,還找不到,就報錯。
-F<directory>
: 在指定目錄尋找framework
,framework search path
-framework <framework_name>
: 指定鏈接的framework名稱
,other link flags -framework AFNetworking
test.m編譯成test.o過程
- 使用OC
- 生成指定架構的代碼,
Big Sur
是:x86_64-apple-macos11.1
,之前是:x86_64-apple-macos10.15
。iOS模擬器是:x86_64-apple-ios14-simulator
。更多內(nèi)容可以參考target部分。 - 使用ARC
- 使用的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
模擬器是:/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.2.sdk \
更多內(nèi)容可以參考sdk部分。 - 用到的其他庫的頭文件地址在./Frameworks
命令示例:
clang -x objective-c \
-target x86_64-apple-ios14-simulator \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.2.sdk \
-I ./AFNetworking \
-c test.m -o test.o
test.o鏈接生成test可執(zhí)行文件
clang鏈接.a
靜態(tài)庫
順序和生成.o
差不多,不需要指定語言。
命令示例:
clang -target x86_64-apple-ios14-simulator \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator14.2.sdk \
-L./AFNetworking \
-lAFNetworking \
test.o -o test
ld鏈接.framework
靜態(tài)庫
ld -syslibroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk \
-lsystem -framework Foundation \
-lAFNetworking \
-L.AFNetworking \
test.o -o test