iOS 中常用的库文件格式 有 .a .dylib .framework .xcframework,今天我们来探索一下 .a文件,也就是静态库。
.a文件
查看.a文件信息
首先我们先看下 .a文件是什么,我们来查看 AFNetWorking里面的.a信息。
bel@beldeMacBook-Pro AFNetworking % file libAFNetworking.a
libAFNetworking.a: current ar archive
我们可以看到它其实是一个文档格式
man ar: 查看 ar 的含义
NAME
ar -- create and maintain library archives
SYNOPSIS
ar -d [-TLsv] archive file ...
ar -m [-TLsv] archive file ...
ar -m [-abiTLsv] position archive file ...
ar -p [-TLsv] archive [file ...]
ar -q [-cTLsv] archive file ...
ar -r [-cuTLsv] archive file ...
ar -r [-abciuTLsv] position archive file ...
ar -t [-TLsv] archive [file ...]
ar -x [-ouTLsv] archive [file ...]
我们可以看到 ar 可以创建并修改归档文件
拆分为.o 文件
我们将.a文件拆开
bel@beldeMacBook-Pro 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文件的合集。
链接静态库
我们新建一个文件夹,将AFNetWorking导入,并新建一个test.m文件
在test.m中,我们添加如下代码
#import <Foundation/Foundation.h>
#import <AFNetworking.h>
int main(){
AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];
NSLog(@"testApp----%@", manager);
return 0;
}
生成目标文件
我们将 test.m编译为 .o文件。
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
ps;iOS开发交流技术群:欢迎你的加入,不管你是大牛还是小白都欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长
-x: 指定语言类型。
-target: 指定架构。
-fobjc-arc: ARC环境。
-isysroot: 编译的SDK路径。
-I :指定头文件位置, 对应Xcode中的 header search path。
-c xxx.m -o xxx.o: 将.m文件编译为 .o 文件。
这样我们就将 test.m编译为了test.o文件。
链接目标文件生成可执行文件
我们在编译的时候已经指明了语言,在链接的时候就不用指定语言了
clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk \
-L./AFNetworking \
-lAFNetworking \
test.o -o test
-L< dir > : 指定库文件路径(.a.dylib库文件),对应Xcode中的Library search Path
-l< libarayr_name>:指定链接的库文件名称(.a.dylib库文件)。对应Xcode中的 other link flags
-L,-l的查找规则为先找 lib + < library_name>的动态库,找不到,再去找 lib+< library_name>的静态库 还找不到,就报错。
链接的过程就是将重定位表中的符号进行重定位,用来绑定真实的符号地址。链接文件的三要素为1,库文件的头文件位置。2,库文件路径。3,库文件名称。
ps;iOS开发交流技术群:欢迎你的加入,不管你是大牛还是小白都欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长
静态库原理
我们新建一个 StaticLibrary文件
并新建TestExample文件,在.m 文件中,我们只是简单输出一段字符
@implementation TestExample
- (void)test:(_Nullable id)e {
NSLog(@"TestExample----");
}
@end
1,将 TestExample.m 文件编译为.o 文件
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 \
-c TestExample.m -o TestExample.o
2,我们将 TestExample.o的名字修改为 TestExample.dylib,最后,将 .dylib去掉
我们使用 file 命令来查看当前文件
file libTestExample.dylib
libTestExample.dylib: Mach-O 64-bit object x86_64
即使我们将名字改掉了,其本身还是一个目标文件。
现在我们使用 test.m 文件去链接 libTestExample看它是否能够正常运行,如果能正常运行,我们就从侧面验证了 静态库文件是.o文件的合集。
ps;iOS开发交流技术群:欢迎你的加入,不管你是大牛还是小白都欢迎入驻 ,分享BAT,阿里面试题、面试经验,讨论技术, 大家一起交流学习成长
我们将test.m 编译为 test.o文件
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./StaticLibrary \
> -c test.m -o test.o
3,用 test.o 链接 libTestExample
clang -target x86_64-apple-macos11.1 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \
-L./StaticLibrary \
-lTestExample \
test.o -o test
链接成功之后 4,运行 test可执行文件,在终端中 输入 lldb进入lldb环境
5,在lldb中运行
(lldb) file test
Current executable set to '/Users/bel/Desktop/静态库原理/test' (x86_64).
(lldb) r
Process 99621 launched: '/Users/bel/Desktop/静态库原理/test' (x86_64)
2021-03-06 23:37:58.265213+0800 test[99621:13587736] testApp----
2021-03-06 23:37:58.265760+0800 test[99621:13587736] TestExample----
Process 99621 exited with status = 0 (0x00000000)
(lldb)
我们的可执行文件可以正常运行
这说明我们的静态库文件链接成功了,就说明了静态库是.o文件的合集。
静态库合并
我们前面讲到,静态库就是.o文件的合集,我们可以把多个静态库的.o文件合并到一起,对静态库进行合并,接下来,我们将 libAFNetworking.a和libSDWebImage.a两个静态库进行合并。
我们先来查看下 libtool命令的含义
man libtool
NAME
libtool - create libraries
ranlib - add or update the table of contents of archive libraries
该命令用来创建库文件或者更新一系列的静态库文件
libtool \
-static \
-o \
> libAll.a \
> ./libAFNetworking.a \
> ./libSDWebImage.a
这样就完成了静态库的.o 文件的合并。
静态库与Framework
.framework 对于静态库文件来说就是.a文件加headers的集合,我们按照.framework的样式,添加如下文件。
我们先将 test.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./Frameworks/TestFramework.framework/Headers -c test.m -o test.o
然后对framework进行链接
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
-F< directory>: 在指定目录寻找framework。
-framework < framework_name> 指定链接的framework名称
附录
本文使用的Clang指令汇总
clang命令参数:
-x: 指定编译文件语言类型
-g: 生成调试信息
-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
lldb相关指令