常见的库文件格式
- .a 静态库
- .dylib 动态库
- .frmawork 静态库/动态库
- .xcframework 2018年apple推出的,不同架构下的framework的封装
.a静态库
先通过file命令来查看.a 文件是什么格式
file {fileName}
其实
.a就是一个文档格式. 众所周知.a文件其实就是.o文件的一个合集. 那么怎么验证这一点呢?介绍另一个命令ar
ar : create and maintan library archives// 压缩目标文件,并对其进行编号和索引,形成静态库。同时也可以解压缩静态库,查看有哪些目标文件
ar -rc a.a a.o
-r: 像a.a添加or替换文件
-c: 不输出任何信息
-t: 列出包含的目标文件
通过ar -t命令验证如下
.a文件是.o文件的合集
编译
clang 命令
- clang 命令: clang is a C, C++, and Objective-C compiler which encompasses preprocessing, parsing, optimization, code genera-tion, assembly, and linking. 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
编译链接
接下来我们链接AFNetworking这个静态库。在本地test.m中有如下代码
通过如下命令
clang -x objective-c \
-target x86_64-apple-macos10.15 \ /Applications/Xcode11.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk \
-I./AFNetworking \
-c test.m -o test.o
执行上述命令之后,生成了一个test.o(目标文件)文件
clang -target x86_64-apple-macos10.15 \
-fobjc-arc \
-isysroot /Applications/Xcode11.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk \
-L./AFNetworking \
-lAFNetworking \
test.o -o test
可能上述命令有点疑惑,为什么链接库的时候不写成libAFNetworking
test.o链接libAFNetworking.a生成test可执行文件
-L./AFNetworking 在当前目录的子目录AFNetworking查找需要的库文件
-lAFNetworking 链接的名称为libAFNetworking/AFNetworking的动态库或者静态库
查找规则:先找lib+<library_name>的动态库,找不到,再去找lib+<library_name>的静态库,还找不到,就报错
链接库成功的三要素:
- 指定头文件路径 (header search path)//头文件路径不一定跟库的路径在一起
- 指定库的路径 (Libraty search path)
- 指定库的名字
编译静态库
在本地目录下有这样两个文件,里面定义了一个类,类的定义很简单,里面一个TestExample 类,类里面随便定义了一个函数
现在将这个文件编译成一个
.a静态库,通过如下命令生成目标文件
clang -x objective-c \
-target x86_64-apple-macos10.15 \
-fobjc-arc \
-isysroot /Applications/Xcode11.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk \
-c TestExample.m -o TestExample.o
为了证明.a文件就是.o文件的合集,我们将生成的TestExample.o更改为libTestExample.dylib
test.m中代码如下
我们将test.m 文件编译成可执行文件
clang -x objective-c \ \
-target x86_64-apple-macos10.15 \
-fobjc-arc \
-isysroot /Applications/Xcode11.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk \
-I./StaticLibrary \
-c test.m -o test.o
再link称为一个可执行文件
clang -target x86_64-apple-macos10.15 \
> -fobjc-arc \
> -isysroot /Applications/Xcode11.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk \
> -L./StaticLibrary \
> -lTestExample \
> test.o -o test
双击执行test, 发现运行一切正常,那么这就说明了.a文件就是.o文件的合集。
静态库合并
介绍一个命令libtool
libtool: create libraries
在本地目录准备两个静态库
运行如下命令
libtool \
> -static \
> -o \
> libML.a \
> /Users/meilinli/selfcode/TestLib/staticlibraryMerge/libAFNetworking.a \
> /Users/meilinli/selfcode/TestLib/staticlibraryMerge/libSDWebImage.a
就得到了一个libML.a的静态库
libtool 命令参数:
-static //合并的是静态库
-o //合并后的名称
framework
- framework: Framework实际上是一种打包方式,将库的二进制文件,头文件和有关的资源文件打包到一起,方便管理和分发。
Framework 和系统级别的framework(Fundation.framework之类的)是有很大的区别的。系统的framework不需要拷贝到目标程序中,我们自己创建的framework哪怕是动态的,最后也需要拷贝到app中,因此apple又将这种Framework称为embedded framework.
从之前生成可执行文件的过程来看,其实头文件搜索路径不一定需要跟library搜索路径一样,framework也是利用了这一点。
先通过clang生成了目标文件TestExample.o
运行命令
ar -rc libTestExample.a TextExample.o 得到一个libTestExample.a静态库, 然后创建一个TestExample.framwork的文件夹,在此目录下创建一个Headers的文件夹,将TestExample.hcopy到Headers 目录下,并且将libTestExample.a 去掉后缀,删除前面lib放到TestExample.framwork下面,我们就手工生成了一个静态的framework. 结果如下图所示
那么重新编译
test.m 使用如下命令
clang -x objective-c -fmodules \
-target x86_64-apple-maos10.15 \
-isysroot /Applications/Xcode11.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk \
-I./Frameworks/TestExample.framwork/Headers \
-c test.m -o test.o
再链接生成test 可执行文件
clang -target x86_64-apple-macos10.15 \
-fobjc-arc \
-isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk \
-F./Frameworks \
-framework TestExample \
test.o -o test
shell
之前敲这些命令比较费事,可以创建一个shell脚本
SYSROOT=/Applications/Xcode11.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.15.sdk
FILE_NAME=test
HEADER_SEARCH_PATH=./StaticLibrary
echo "-----开始编译test.m"
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 ./StaticLibrary #进入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 #退出StaticLibrary目录
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}
dead code strip
当我们只是import头文件,但是不使用的时候,编译之后,会有那个头文件的符号吗?
编译生成可执行文件,通过
objdump查看下mach-o文件中的text段
可以看到text段并没有TestExample.h 中的符号. 把之前注释的代码放开,又是什么情况呢?
可以看到,text段边长了。并且用到的符号也在text段。可以得到一个结论clang在链接的过程中
Dead code strip默认是生效的。但是这个时候可能会有一些问题,对于分类来说,分类是在runtime的时候创建的。举个例子说明,创建个一个静态库, 详见github链接
静态库中有一个分类
MLOneObject+ML, 中有一个方法test_category, 在MLOneObject中的test方法调用test_category.
创建一个可执行文件工程,link MLTestFramework.framework.
运行起来之后,出现crash
那么怎么解决这个问题呢?可以通过配置链接器,如下图所示
上图中,指定了链接
MLTestFramework.framework的时候是force_load,不会strip dead code.
当链接静态库的时候,strip dead code 不生效的设置方法
- OTHER_LDFLAG = -XLinker -force_load ${STATIC_LIBRARY_PATH} //link 指定静态库的时候strip dead code不生效
- OTHER_LDFLAG = -XLinker -ObjC //所有的oc的代码strip dead code不生效
- OTHER_LDFLAG = -XLinker -all_load //link所有的
Mach-O的时候,strip dead code不生效
上述option都是针对的静态库的链接。当然还可以通过连接器参数查看为什么一个符号没有被strip。
-Xlinker -why_live -Xlinker _global_function //_global_function 是去查看的符号
在一个target为可执行文件的工程中,里面有一些并没有用到的类,并且在工程中设置了dead code strip 为 YES, 但是最终并没有把这些没有用到的类的符号strip掉。
可以设置为
Monolithic将这些没有用到的符号strip掉。