Swift开发IOS面试专题

iOS 静态库与动态库的一些面试题

2021-04-28  本文已影响0人  just东东

静态库与动态库的一些面试问题

关于动/静态库一些问题的随笔,写的比较乱,啊哈哈哈哈哈😄

常用的文件格式

.a .dylib .framework .xcframework

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

nm -pa path

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

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

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

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

在终端执行man ldld是链接器。)命令可以看到下面这段话:

image

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

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

总结一下答案:

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

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

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

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

如果您对Mach-O感兴趣,可以看看我的这篇文章Mach-O探索

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

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

image

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包中各自打包、签名、加载一份。

image

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之间没有什么联系。以下四个是对于加载静态库的四个级别

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

那么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 动态库 -> 动态库 -> 符号,所以动态库的符号不会冲突。

source

上一篇下一篇

猜你喜欢

热点阅读