美文网首页iOS之道
静态库与动态库的基础概念

静态库与动态库的基础概念

作者: 没戏还在演戏 | 来源:发表于2021-08-06 15:25 被阅读0次

    1. 动态库和静态库的区别是什么?

    动态库所有符号信息都放到一起,
    静态库按照.o文件进行分开存储

    动态库的代码都是统一放在 Mach-O text section 段的
    静态库的代码也是根据.o文件分开存储的


    AF的静态动态图.png

    所以说静态库是一个.o文件的合集,静态库在往后链接就是可执行文件,也可以说是(动态库)。

    静态库:.o -> .o合集 -> .a(静态库) | 静态库只到这里 链接 -> 可执行文件(动态库)

    • 静态库: 是.o文件的合集,在每个.o里面都包含了全局符号,ld在需要解析一些符合的时候,才会从将这些符号从.o中链接到主App中。

    • 动态库:是一个最终链接完成的镜像,也就是编译链接的最终产物。所以动态库不可以做合并,所以在arm64和armv7同时支持的动态库也不是合并的,只是放在一起,Mach-O根据不同的环境针对性的链接。

    所以就编译后的体积来说,同样的代码编译成动态库要小于静态库的体积。但是有时候从文件上来看并不是这样,这是因为静态库里面的.o文件并没有达到对齐所在的空间,所以存在比动态库小的可能。一般就是小于16k的时候,代码不多 。

    查看命令 objdump --macho -x 文件路径 查看他的Mach-O段。

    总结一下答案:

    静态库也就是静态链接库,可以简单的看成目标文件的集合。即很多目标文件经过压缩打包后形成的文件。Windows下的.lib,Linux和Mac下的.a。Mac独有的.framework。

    缺点: 浪费内存和磁盘空间,模块更新困难。

    动态库:与静态库相反,动态库在编译时并不会被拷贝到目标程序中,目标程序中只会存储指向动态库的引用。等到程序运行时,动态库才会被真正加载进来。格式有.framework .dylib .tbd。现在苹果允许上架的动态库类型是.framework,有签名,阻止了热更新。

    缺点:会导致一些性能损失。但是可以优化,比如延迟绑定(lazy Binding)技术。

    2.静态库链接到主程序,静态库存放在什么位置?动态库呢?

    静态库会在编译连接后与APP主程序合并成一个文件,也就是在我们的APP可执行文件中,动态库由于不能再合并了则需要单独签名放在frameworks文件夹中,在APP主程序文件中,保存着动态库的引用路径,等到APP运行的时候在进行加载。

    3. 静态库、动态库与framework的关系?

    静态库就是一些.o文件的合集,也就是静态链接库,一般是.a文件或者.framework文件,一般在APP包中看不到静态库,都会被合并的主程序可执行文件中。

    动态库一般是.framework .dylib .tbd文件形式。动态库在编译时并不会被拷贝到目标程序中,目标程序中只会存储指向动态库的引用。等到程序运行时,动态库才会被真正加载进来

    framework 是Mac OS/iOS 平台上的一种打包方式,将库的二进制文件,头文件和有关的资源文件打包到一起,方便管理和分发。

    Framework和系统的 UIKit.Framework还是有很大区别的。系统的Framework不需要拷贝到目标程序中,我们自己做出来的Framework哪怕是动态的,最后也还是要拷贝到APP中(APP和Extension的Bundle是共享的),因此苹果又把Framework称为Embedded Framework。

    Embedded Framework 也就是嵌入式Framework,开发中使用的动态库放入到ipa下的frameworks目录下,基于沙盒运行。不同的APP使用相同的动态库,并不会只在系统中存在一份。而是会在多个APP包中各自打包、签名、加载一份。

    4. 什么是xcframework,使用有什么优势?

    Xcode 会自动根据架构来将对应的架构拷贝到ipa中。Xcode 11以上支持。

    XCFramework:是苹果官方推荐的、支持的,可以更方便的表示一个多个平台和架构的分发二进制库的格式。

    需要Xcode11以上支持。
    是为更好的支持Mac Catalyst和ARM芯片的macOS。
    专门在2019年提出的framework的另一种先进格式。

    目前支持:
    iOS/iPad:arm64
    iOS/iPad Simulator:x86_64 arm64
    Mac Catalyst:x86_64 arm64
    Mac:x86_64 arm64

    和传统的framework相比:

    1. 可以用单个.xcframework文件提供多个平台的分发二进制文件;
    2. 与Fat Header相比,可以按照平台划分,可以包含相同架构的不同平台的文件;
    3. 在使用时,不需要再通过脚本去剥离不需要的架构体系。

    关于Fat Header就是胖二进制文件(Mach-O)的头,也就是Mach-O Universal Files,通过header来记录不同架构在文件中的偏移量,segment占多个分页。所以对于多个架构这个header就会变得很大,也就是为什么要被叫做Fat Header

    5. 什么是dead strip与-Objc参数与-force_load之间有联系吗?

    dead strip与-Objc和-force_load之间没有什么联系。以下四个是对于加载静态库的四个级别

    • -noall_load:默认级别,也就是我们的APP默认不加载(链接)静态库中的内容,什么都没用到就不加载
    • -all_load:把所有代码全部加载进来,此时就可以看到静态库中的所有符号
    • -ObjC:只加载与OC相关的代码
    • -force_load <file>:指定的静态库才会被加载进来,可以通过这个来控制静态库的符号冲突,哪个作为主链接,哪个由ld扫描,不需要加载的就不加载了。当然解决符号冲突还可以通过加前缀的方式。

    以上四个只针对我们的链接器去链接我们的静态库的时候使用。

    那么dead strip是干什么的呢?我们通过man ld命令在终端里找到如下:

    image

    所有的.o文件,包括动态库都会生效。删除没有被入口函数(默认是main)访问的函数,或者导出这些符号。导出符号也就是其他文件的导入(#import)。我自己没用,但是导出后别人用了,就是这个意思。

    也就是你定义了一个函数,但是并没有被入口函数使用,所以就在编译链接的时候干掉这些方法。再就是没有成为导出符号,也就是对于其他文件通过#import。这是对每个文件都生效的。

    如果一个动态库也没有被主函数使用,也没有被导出符号。

    image

    意思一个动态库没有被主函数使用,也没有被导出符号,则移除这个动态库。链接信息剥离掉,默认不剥离。

    image

    为什么要加载.o,为什么要加载这个符号。

    -noall_load 和 -all_load -> 链接器ld 加载静态库 -> App 没有用到静态库的代码,以类为最小单位,没有用到就不加载,如果是分类有时候是没办法判断用没用到,所以一下三方库建议我们使用-Objc,但是最好使用-force_load <file>,可以有效减少包体积。

    6. 什么是tbd文件,在日常开发中有哪些应用场景?

    tbd全称是text-based stub libraries,本质上就是一个YAML描述的文本文件。

    他的作用是用于记录动态库的一些信息,包括导出的符号、动态库的架构信息、动态库的依赖信息

    用于避免在真机开发过程中直接使用传统的dylib

    对于真机来说,由于动态库都是在设备上,在Xcode上使用基于tbd格式的伪framework可以大大减少Xcode的大小。

    tbd格式文件,本身是通过Xcode内置工具tapi-installapi专门来生成的,具体路径为:

    /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefaul t.xctoolchain/usr/bin/tapi installapi
    
    

    7. 要减小APP的体积,应该使用静态库还是动态库,为什么?

    一般使用静态库会减小APP体积。

    静态库是.o文件的合集,在每个.o里面都包含了全局符号,.o多了就包含了多个全局符号,所以体积就大了。

    动态库是最终的编译链接产物,所有符号都放在一起,减少了全局符号存储的次数,所以体积就小。

    那么为什么还要使用静态库呢?因为静态库还存在一层编译连接的的过程,会将静态库合并到APP主程序文件中,可以优化,而动态库已经是编译链接的最终产物,还需要拷贝到frameworks文件夹中,所以会增加APP的体积。

    其他

    模仿生成APP的过程

    .m -> .o .m -> .a 静态库 -> 链接去生成APP

    静态库链接生成APP倒地存储在什么地方?

    静态库的代码与主程序是放在一起的。

    APP链接

    1. 找到头文件,api
    2. 找到库所在的位置
    3. 找到库的名称

    1. 怎么判断什么场景使用动/静态库?
    静态库:ipa体积小,不影响启动速度,但是静态库不能完全剥离符号,只允许剥离调试符号,静态库暴露出来的信息要多一些
    动态库:资源文件更好处理,更加灵活,如果你不想暴露更多信息可以使用动态库,比如你是做SDK开发的。

    2. 动态库超过6个?加载变慢怎么处理

    1. 改成静态库
    2. 动态库加载: execve Mach-O -> dyld -> load
    
    

    3. 静态库符号冲突?
    1. 使用-force_load指定两个冲突的静态库那个完全加载,那个由ld判断要加载的代码
    2. 符号冲突,不叫同名符号就可以
    1. 有源码,直接修改
    2. 没有源码,objcopy->重命名一个符号
    3. brew install llvm
    4. --redefine-sym=old=new

    4. all_load等只针对静态库

    all_load只针对静态库,不针对动态库。

    5. A静态库引用的B静态库,在项目中报找不到B中的符号?

    A中只保存了B静态库的头文件,没有保存符号真正的实现代码,或者说没有保存符号真正代表的代码。
    解决办法就是:

    1. 合并两个静态库
    2. 把B静态库也加载到APP中

    6. 检测没有用到的代码(方法)?

    个人觉得可以使用clang插桩,将用到的符号都检测出来,比如二进制重排的时候的order文件,然后对比所有符号。

    或者参考上面这些dead strip等实现原理,做一些研究。

    7. embed

    只要是framework都可以embed,无关乎动/静态库

    framework只是一种打包格式,里面可以包含一些资源文件。

    8. hmap

    .o -> 头文件目录/子目录 -> 特定的头文件 -> 花费一定的时间
    hmap -> {特定的头文件:特定的头文件地址} -> 编译速度提升
    特定的二进制结构

    9. Two-level namespace

    两级命名空间,默认的动态库是由两级命名空间的。

    静态库为什么会符号冲突,就是静态库默认是取符号表找符号,所以两个同名符号就是会冲突。

    Two-level namespace 动态库 -> 动态库 -> 符号,所以动态库的符号不会冲突。

    参数详解:
    只针对连接器链接静态库
    -noall_load: 默认不加载所有代码到动态库
    -all_load: 把所有代码加载到动态库里
    -Objc: 只加载OC相关的代码
    -force_load <file>: 指定一个静态库 指定的静态库才会完全加载进来

    noall_load + all_load -> ld + 静态库 ->App有没有用到静态库的代码|类

    -dead_strip:所有.O文件包括动态库链接到APP的时候都会生效 针对使用关系去配置 针对到每个文件 而上面4个参数只针对连接器链接静态库这一个场景
    -Xlinker -dead_strip \ 会忽略未使用的方法 ld -〉针对场景 1. 没有被入口函数使用的 2. 不能成为导出符号

    -Xlinker -why_live -Xlinker _global_function \ 打印出这个方法被谁使用的

    相关文章

      网友评论

        本文标题:静态库与动态库的基础概念

        本文链接:https://www.haomeiwen.com/subject/wyobvltx.html