注册
iOS

iOS整理: 关于动态库和静态库

之前对于这两者的概念仅仅停留在八股文的认知水平(可能八股都答的一塌糊涂)亦或者就是道听途说,知道下怎么用就完事儿了,看了很多相关的资料,看了就忘,索性自己整理一下,理顺一下自己的思路,体系化的理解一下,防止自己变成脑残。。。


在此之前,我们对一些常识性的东西复习一下


.a .framework

.a 是单纯的二进制文件,.framework是二进制文件+资源文件。


程序执行的流程


预处理--->编译--->汇编--->链接(汇编程序生成的目标文件并不能被立即执行,还需要通过链接器(Linker),将有关的目标文件彼此相连接,使得所有的目标文件成为一个能够被操作系统载入执行的统一整体。)

  • 静态链接直接在编译阶段就把静态库加入到可执行文件当中去。优点:不用担心目标用户缺少库文件。缺点:最终的可执行文件会较大;且多个应用程序之间无法共享库文件,会造成内存浪费。
  • 动态链接在链接阶段只加入一些描述信息,等到程序执行时再从系统中把相应的动态库加载到内存中去。优点:可执行文件小;多个应用程序之间可以共享库文件。缺点:需要保证目标用户有相应的库文件。

关于iOS应用的启动流程


1. 解析Info.plist


2. Mach-O(可执行文件)加载


dylib loading time


rebase/binding time


3. 程序执行


....

这里其实还是想简述一下加载流程,因为我在这儿一直也有个误区,应用在启动前静态库已经存在于可执行的二进制文件当中了,而动态库在启动后才进行加载等一系列操作。


为什么要阐述这些老生常谈的东西呢,因为我以前一直对动态库的加载和编译时机存在误解,我们拿一个具体的工程举例 


54419dc2f7cd42e45e0c74a76542cbe6.png

268d83ee8291996605e1e67adc975664.png

我们从产物的包内容找到一路找到可执行文件,可以看到可执行文件和framework是单独存在的
静态库在编译的时候就被打到二进制文件当中了


怎么区分动态库还是静态库


一般来说,动态库以 .dylib 或者 .framework 后缀结尾;静态库以 .a 和 .framework 结尾。
这里列出几种方法区分动态库还是静态库

  1. 查看Mach-O Type来区分
  2. 查看ipa的目录结构
  3. 通过file工具查看

动态库/静态库的加载过程 & 两者之间的区别


一般来说,build一个项目的过程是先compile然后再link,然后才有一个可执行文件。link的时候要做的一件事情就是把各种函数符号转换成函数调用地址,然后最终生成的可执行文件就能够直接调用到函数了。


fc8cf4589473b6f8ccd9eeabac023306.png


1.静态库在build的时候就把库里面的代码链接进可执行文件。这里还要再补充一句,会将静态库中 被使用的部分 都添加到应用程序的可执行文件,这意味着应用程序的可执行文件大小会随着静态库数量的增加而增大。在运行时,静态库会随着应用程序的可执行文件一起加载到同一代码区。在 iOS 开发中,应用程序的可执行文件就是 ipa 解压后,包内容中与 app 同名的可执行文件


6decbd3322cb79cc3eb633903d4e71a0.png


2.动态库的做法不一样,不会在build的时候就把代码link进可执行文件,这里我们只对动态链接库进行阐述
对于动态链接库而言,build可执行文件的时候需要指定它依赖哪些库,当可执行文件运行时,如果操作系统没有加载过这些库,那就会把这些库随着可执行文件的加载而加载进内存中,供可执行程序运行。如果多个可执行文件依赖同一个动态链接库,那么内存中只会有一份动态链接库的代码,然后把它共享给所有相关可执行文件(APP)的进程使用,所以它也叫共享库


那简言之:动态链接库在可执行文件得到运行的时候就加载 这句话很有营养


在ios程序的启动流程中,我们会先加载应用的可执行文件(这就包括了静态库文件)然后才是动态库的一系列加载流程(程序执行
静态库:链接时完整地拷贝至可执行文件中,被多次使用就有多份冗余拷贝,存在形式:.a和.framework
动态库:链接时不复制,程序运行时由系统动态加载到内存,供程序调用,系统只加载一次,多个程序共用,节省内存。存在形式:.dylib和.framework


之前对这里一直存在误区,这里的加载是以可执行文件(APP)为单位的。还有就是我们这里谈的动态库都都是系统层面的动态库,区别也是针对于静态库和系统动态库而言的。另外就是静态库在一开始就存在于可执行文件中,而动态库在运行时动态的进行绑定。


use_frameworks!


podfile中经常会加上这句话,我们来看一下实际的作用和效果
当使用 use_frameworks的时候 cocoapods会生成对应的 frameworks 文件(动态库)
在Link Binary With Libraries:会生成Pods_工程名.framework,包含了其它用cocoapods导入的第三方框架的.framework文件


5618ade876e88ddb73b8dca99f278f2b.png


当不使用use_frameworks!(静态库)cocoapods会生成相应的 .a文件(静态链接库)
Link Binary With Libraries: libPods-工程名.a 包含了其他用cocoapods导入有第三库的 .a 文件


132e7ef5829305fb7e91531ad738ca26.png


当然我还注意到一些其他文件的diff 比较令我好奇的就是这个modulemap 之前也没了解过,以后有机会研究一下


cc8c053bef37d3d54df6d840ae2b9fa5.png


Xcode Embed


1a719dfc33ccf1e05fd200d61a65f830.png


对于这个设置,之前也是不太清楚,

  • 对于 系统动态库,可以将 Embed 属性设置成 Do Not Embed,因为 iOS 系统提供了相关的库,我们无需将它们再嵌入到应用程序的 ipa 包中,如:Foundation.frameworkUIKit.framework
  • 对于 用户动态库,需要将 Embed 属性设置成 Embed,因为链接发生在运行时,链接器需要从应用程序的 ipa 包中加载完整的动态库。
  • 对于 静态库,需要将 Embed 属性设置成 Do Not Embed,因为链接发生在编译时,编译完成后相关代码都已经包含在了应用程序的可执行文件中了,无需在应用程序的 bundle 中再保存一份。

动态库和静态库的使用场景


静态库

  1. 静态库主要应用于模块化,分工合作
  2. 避免少量改动经常导致大量的重复编译连接
  3. 也可以重用,注意不是共享使用

动态库


1.使用动态库,可以将最终可执行文件体积缩小


2.对于 iOS 开发来说, 因为我们只能使用 Embedding Frameworks 来使用动态库, 这样的动态库并不是真正的动态库, 其会在编译时全部置入 app, 然后再 app 启动时全部加载, 这样的话会导致体积大, 加载速度慢.


动静态库的混用

  • 静态库可以依赖静态库
  • 动态库可以依赖动态库
  • 动态库不能依赖静态库! 动态库不能依赖静态库是因为静态库不需要在运行时再次加载, 如果多个动态库依赖同一个静态库, 会出现多个静态库的拷贝, 而这些拷贝本身只是对于内存空间的消耗

但其实两者之间都是可以通过各种操作进行依赖的
静态库也是可以依赖动态库的
动态库也是可以依赖静态库的


总结


以上就是我对动态库以及静态库一些盲区的的具体总结和详细分析,总的来说,对每个角色的定位,有了更清晰的认知


补充


用户创建伪动态库 和静态库有什么区别呢,如果有区别 具体是怎么应用的呢 有知道的朋友可以帮我解释下吗?不胜感激


参考链接


blog.csdn.net/GeekLee609/…


juejin.cn/post/704110…


zhuanlan.zhihu.com/p/346683326


http://www.jianshu.com/p/662832e16…


chuquan.me/2021/02/14/…


zhuanlan.zhihu.com/p/346683326


作者:脑残coder
链接:https://juejin.cn/post/7262558218168729660
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册