动态库与静态库上 (3)
终端命令补充以及Common Symbol
扩展
1. 查看符号命令 man nm 1. /[关键字] 去搜索,例:/-p 1. n -> 去下一个搜索的选项 2. N -> 去上一个搜索的选项 3. q -> 退出搜索 2. 查看符号命令 nm --help //不排序, 符号表中的顺序 nm -pa text.o
image.png
- Common Symbol : 未定义的全局符号
.a与.framework静态库详解
常用库文件格式
.a静态 .dylib动态 .framework静态动态 .xcframework2018年推出不同架构的库
库 -> 说白了就是一段编译好的二进制代码, 加上头文件就可以供别人使用.
什么时候会用到库(Libraty)?
- 某些代码需要给你别人使用, 但是我们不希望别人看到代码, 就需要以库的形式进行封装, 只暴露出头文件.
- 对于某些不会进行大的改动的代码, 我们想减少编译的时间, 就可以把它打包成库, 因为库是已经编译好的二进制了, 编译的时候只需要Link一下, 不会浪费编译时间.
静态库
1. 链接静态库AFNetworking -> 进入AFNetworking -> file libAFNetworking.a 1. libAFNetworking.a -> archive文档格式 2. man ar 查看ar的意思 -> ar -t libAFNetworking.a -> 显示.o文件 2. 静态库AFNetworking 链接 test.m 生成目标文件 1. man clang 查看clang定义 2. clang -x objective-c \ //指定编译的语音 -target x86_64-apple-macos11.1 \ //指定编译的平台 -fobjc-arc \ //arc的环境 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX11.1.sdk \ //指定SDK的路径, 此处是使用的内置的基础库, 需要链接的基础库的路径 -I./AFNetworking \ //指定链接的库头文件的路径 -c test.m -o test.o //生成目标文件 3. 通过链接器 生成可执行文件 1. 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 \ //.o文件的路径, 因为符号保存在.o文件里, -L指定库文件路径(.a.dylib库文件)-lAFNetworking \ //指定链接的库文件名称(.a.dylib库文件)test.o -o test 1. -lAFNetworking 链接的名称为libTestExample/TestExample的动态库或者静态库,查找规则:先找lib+<library_name>的动态库,找不到,再去找lib+<library_name>的静态库,还找不到,就报错
链接一个库文件的三要素
-I<directory> 在指定目录寻找头文件 -> header search path -L<dir> 指定库文件路径(.a.dylib库文件)-> library search path -l<library_name> 指定链接的库文件名称(.a.dylib库文件)-> other link flags
静态库,.o文件的合集(链接静态库生成可执行文件)
验证静态库是.o文件合集
1. TestExample.m -> TestExample.o 2. TestExample.o -> 改名字libTestExample.dylib(目的变成可执行文件) -> 改名字libTestExample 3. test.m 链接 libTestExample 1. 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. lldb -> 进入lldb的环境 5. file test -> 将test可执行文件包装Target\ 6. r -> 证明静态库就是.o文件的合集 7. q -> 退出lldb
扩展
man objdump -> 输出文件信息 objdump --macho --private-header libTestExample.dylib
静态库合并
把所有的.o文件拿过来放在一个.o文件里面
1. man libtool 2. libtool -static -o <OUTPUT NAME> <LIBRARY_1> <LIBRARY_2> 1. -static \ //合并类型 2. libtool -static -o libCat.a 库A 库B -> 库A 库B分别解压, 然后合并 1. 有关重复文件, libtool会帮助处理 2. 头文件处理,问题所在,怎么处理
mudule
mudule 是clang提供, 来专门处理头文件的. mudule的作用, 预先将.h文件 -> 二进制, 然后缓存(那么每个引入的其他类的头文件,就不用每次都编译, 可以直接拿来用)
链接器的特性,
Auto-Link
。启用这个特性后,当我们import <模块>
,不需要我们再去往链接器去配置链接参数。比如import <framework>
我们在代码里使用这个是framework格式的库文件,那么在生成目标文件时,会自动在目标文件的Mach-O
中,插入一个load command
格式是LC_LINKER_OPTION
,存储这样一个链接器参数-framework <framework>
。
Framework
Framework 实际是一种打包方式, 将库的二进制文件, 头文件和有关资源打包到一起, 方便管理.
Framework和系统的UIKit.Framework还是有很大区别. 系统的Framework不需要拷贝到目标程序中, 我们自己做出来的Framework哪怕是动态的, 最后也还是要拷贝到APP中(APP和Extension的Bundle是共享的), 因此苹果又把这种Framework称为Embedded Framework.
Framework:
静态库 -> Header + .a + 签名 + 资源文件 动态库 -> Header + .dylib + 签名 + 资源文件
ar命令
ar
压缩目标文件,并对其进行编号和索引,形成静态库。同时也可以解压缩静态库,查看有哪些目标文件:
- ar -rc a.a a.o
- -r: 像a.a添加or替换文件(有就替换, 没有就添加)
- -c: 不输出任何信息
- -t: 列出包含的目标文件
- ar -rc libTestExample.a libTestExample.o -> 生成.a文件(文中这样写,相当于改个格式)
手动创建Framework
1. 创建.framework结尾的文件夹,例TestFramework.framework 2. 新建的文件夹TestFramework.framework -> 创建Headers文件夹 -> 将.h头文件放入 3. 将编译好的库文件放入TestFramework.framework,并修改名字,去除lib开头(如果有),去除.后缀 4. 测试 1. 将需要链接的文件生成目标.o文件 2. clang -x objective-c -fmodules -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 3. 链接Framework 4. 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 \ //-F+Frameworks所在的目录 -framework TestExample \ //要链接的Frameworks的名字 test.o -o test 5. lldb 6. file test 7. r 8. q • -F<directory> 在指定目录寻找framework framework search path • -framework <framework_name> 指定链接的framework名称 other link flags -framework AFNetworking
shell初探
.sh脚本创建
.sh解释型语言
记得给build.h加可执行权限 -> chmod +x ./build.sh -> 然后执行./build.sh
dead_strip与静态库
如果引入头文件, 没有使用, 那么生成的可执行文件, 里面是否包含导入的头文件?
1. objdump --macho -d test 1. -d 查看__TEXT字段的 2. 发现没有 2. 使用一下, 再用脚本再生成一下test, 发现有了 3. clang命令是默认dead_strip的 4. 分类会有问题 -> 运行时动态创建的 1. 查看是否是静态库target -> build Setting -> mach 2. dead_strip链接的时候生效的
workspace
- 可重用性, 多个模块可以在多个项目中使用, 节约开发和维护时间.
- 节省测试时间. 单独模块意味着每个模块都可以添加测试功能.
- 更好的理解模块化思想.
创建workspace
1. File -> Save As workspace -> TestDeadStrip 2. Add File to workspace -> 添加其他project项目到workspace 1. 如果选中的有文件, 那么+号选项没有这个选项 2. 重新从新创建的workspace进入项目 3. 记得先编译库, 再使用库的编译项目 4. 想编译项目的时候,同时编译库文件, 只需将库文件 + 项目的Targets -> General -> Frameworks,Libraries,andEmbedded Content 1. Embed & sign -> Embed嵌入的意思, 即编译的时候把Framework拷贝到IPA包里 2. Do Not Embed -> 不拷贝, 因为此处是静态库(链接的过程已经在一起了,不需要)
接上面分类流程, 会报错, 原因就是dead_strip把分类的代码脱离了.
解决办法:
.o文件的合并与.o文件链接静态库的区别
上节遗留问题
.o 和 .o 链接 : 先合并成一个大的.o, 再链接dead_strip生成可执行文件
.o 和 .a 链接 : 先dead_strip去掉多余的
解决:
Targets -> Build Setting -> LTO -> Link-Time Optimization -> Monolithic