静态库原理

1,152 阅读5分钟

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
  • -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,库文件名称

静态库原理

我们新建一个 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文件的合集

我们将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.alibSDWebImage.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相关指令

file <可执行文件>: 将可执行文件加载到 lldb中
r: 执行可执行文件。