什么是库 ?
库就是程序代码的集合,将N个文件组织起来,是共享程序代码的一种方式。库从本质上来说是一种可执行代码的二进制格式,可以被载入内存中执行。
库的分类
- 开源库:源代码是公开的,可以看到每个实现文件(.m文件)的实现,例如GitHub上的常用的开源库:AFNetworking、SDWebImage等;
- 闭源库:不公开源代码,是经过编译后的二进制文件,看不到具体的实现。闭源库又分为:静态库 和 动态库
1、linux中静态库和动态库区别:
库从本质上来说是一种可执行代码的二进制格式
,可以被载入内存中执行。库分静态库和动态库两种。
静态库:这类库的名字一般是libxxx.a;利用静态函数库编译成的文件比较大,因为整个函数库的所有数据都会被整合进目标代码中,他的优点就显而易见了,即编译后的执行程序不需要外部的函数库支持,因为所有使用的函数都已经被编译进去了。当然这也会成为他的缺点,因为如果静态函数库改变了,那么你的程序必须重新编译。
动态库:这类库的名字一般是libxxx.so;相对于静态函数库,动态函数库在编译的时候 并没有被编译进目标代码中,你的程序执行到相关函数时才调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。由于函数库没有被整合进你的程序,而是程序运行时动态的申请并调用,所以程序的运行环境中必须提供相应的库。动态函数库的改变并不影响你的程序,所以动态函数库的升级比较方便。
2、iOS开发中静态库和动态库区别:
静态库和动态库是相对编译期和运行期的:静态库在程序编译时会被链接到目标代码中,程序运行时将不再需要改静态库;而动态库在程序编译时并不会被链接到目标代码中,只是在程序运行时才被载入,因为在程序运行期间还需要动态库的存在。
静态库 好处:
- 模块化,分工合作,提高了代码的复用及核心技术的保密程度
- 避免少量改动经常导致大量的重复编译连接
- 也可以重用,注意不是共享使用
动态库 好处:
- 使用动态库,可以将最终可执行文件体积缩小,将整个应用程序分模块,团队合作,进行分工,影响比较小
- 使用动态库,多个应用程序共享内存中得同一份库文件,节省资源
- 使用动态库,可以不重新编译连接可执行程序的前提下,更新动态库文件达到更新应用程序的目的。
- 应用插件化
- 软件版本实时模块升级
- 在其它大部分平台上,动态库都可以用于不同应用间共享, 共享可执行文件,这就大大节省了内存。
iOS平台 在 iOS8 之前,苹果不允许第三方框架使用动态方式加载,从 iOS8 开始允许开发者有条件地创建和使用动态框架,这种框架叫做 Cocoa Touch Framework。虽然同样是动态框架,但是和系统 framework 不同,app 中使用 Cocoa Touch Framework 制作的动态库 在打包和提交 app 时会被放到 app main bundle 的根目录 中,运行在沙盒里,而不是系统中。也就是说,不同的 app 就算使用了同样的 framework,但还是会有多份的框架被分别签名,打包和加载。不过 iOS8 上开放了 App Extension 功能,可以为一个应用创建插件,这样主app和插件之间共享动态库还是可行的。
苹果系统专属的framework 是共享的(如UIKit), 但是我们自己使用 Cocoa Touch Framework 制作的动态库是放到 app bundle 中,运行在沙盒中的
静态库和动态库的存在的形式
- 静态库:以.a 和 .framework为文件后缀名。
- 动态库:以.tbd(之前叫.dylib) 和 .framework 为文件后缀名。(系统直接提供给我们的framework都是动态库!)
理解:.a
是一个纯二进制文件,.framework
中除了有二进制文件之外还有资源文件。.a
,要有.h
文件以及资源文件配合,.framework
文件可以直接使用。总的来说,.a + .h + sourceFile = .framework
。所以创建静态库最好还是用.framework
的形式
静态库和动态库的区别
不同点:
- 静态库在链接时,会被完整的复制到可执行文件中,如果多个App都使用了同一个静态库,那么每个App都会拷贝一份,缺点是浪费内存。类似于定义一个基本变量,使用该基本变量是是新复制了一份数据,而不是原来定义的;
- 动态库不会复制,只有一份,程序运行时动态加载到内存中,系统只会加载一次,多个程序共用一份,节约了内存。类似于使用变量的内存地址一样,使用的是同一个变量;
共同点: - 静态库和动态库都是闭源库,只能拿来满足某个功能的使用,不会暴露内部具体的代码信息
静态库的处理方式
- 对于一个静态库而言,其实已经是编译好的了,类似一个 .o 的集合 这里并没有连接。在 build 的过程中只会参与链接的过程,而这个链接的过程简单的讲就是合并,并且链接器只会将静态库中被使用的部分合并到可执行文件中去。相比较于动态库,静态库的处理起来要简单的多,具体如下图:
- 链接器会将所有**.o**用到的 global symbol 和 unresolved symbol 放入一个临时表,而且是 global symbol 是不能重复的。
- 对于静态库的 .o , 连接器会将没有任何 symbol 在 unresolved symbol table 的给忽略。
- unresolved symbol 类似
extern int test();
--- **.h **的 声明? - global symbol 类似
void test() { print("test")}
-- .m 的 实现? - 最后,链接器会用函数的实际地址来代替函数引用。
动态库的处理方式
- 首先,对于动态库而言其实分 动态链接库 和 动态加载库 两种的,这两个最本质的区别还是加载时间。
- 动态链接库:在没有被加载到内存的前提下,当可执行文件被加载,动态库也随着被加载到内存中。在 Linked Framework and Libraries 设置的一些 share libraries。【随着程序启动而启动】
- 动态加载库:当需要的时候再使用 dlopen 等通过代码或者命令的方式来加载。【在程序启动之后】
- 但是不论是哪种动态库,相比较与静态库,动态库处理起来要棘手的多。由于动态库是动态的,所以你事先不知道某个函数的具体地址。因此动态链接器在链接函数的时候需要做大量的工作。
因为动态库在链接函数需要做大量的工作,而静态库已经实现处理好了。所以单纯的在所有都没有加载的情况下,静态库的加载速度会更快一点。而在 iOS 开发中的『库』(一) 提到的有所不妥,正确应该是,虽然动态库更加耗时,但是对于在加载过的share libraries不需要再加载的这个前提下,使用动态库可以节省一些启动时间。
- 而实现这个动态链接是使用了 Procedure Linkage Table (PLT)。首先这个 PLT 列出了程序中每一个函数的调用,当程序开始运行,如果动态库被加载到内存中,PLT 会去寻找动态的地址并记录下来,如果每个函数都被调用过的话,下一次调用就可以通过 PLT 直接跳转了,但是和静态库还是有点区别的是,每一个函数的调用还是需要通过一张 PLT。这也正是 sunny 所说的所有静态链接做的事情都搬到运行时来做了,会导致更慢 的原因。
从源代码到app
当我们点击了 build 之后,做了什么事情呢?
- 预处理(Pre-process):把宏替换,删除注释,展开头文件,产生 .i 文件。
- 编译(Compliling):把之前的 .i 文件转换成汇编语言,产生 .s文件。
- 汇编(Asembly):把汇编语言文件转换为机器码文件,产生 .o 文件。
- 链接(Link):对.o文件中的对于其他的库的引用的地方进行引用,生成最后的可执行文件(同时也包括多个 .o 文件进行 link)。
相关动态库和静态库的创建
动态库动态更新问题
能否动态库的方式来动态更新AppStore上的版本呢?
framework本来是苹果专属的内部提供的动态库文件格式,但是自从2014年WWDC之后,开发者也可以自定义创建framework实现动态更新(绕过apple store审核,从服务器发布更新版本)的功能,这与苹果限定的上架的app必须经过apple store的审核制度是冲突的,所以含有自定义的framework的app是无法在商店上架的,但是如果开发的是企业内部应用,就可以考虑尝试使用动态更新技术来将多个独立的app或者功能模块集成在一个app上面!(我开发的就是企业内部使用的app,我们将企业官网中的板块开发成4个独立的app,然后将其改造为framework文件最终集成在一款平台级的app当中进行使用,这样就可以在一款app上面使用原本4个app的全部功能!)
使用自定义的动态库的方式来动态更新只能用在 in house(企业发布) 和develop 模式却但不能在使用到 AppStore 因为在上传打包的时候,苹果会对我们的代码进行一次 Code Singing,包括 app 可执行文件和所有Embedded 的动态库。因此,只要你修改了某个动态库的代码,并重新签名,那么 MD5 的哈希值就会不一样,在加载动态库的时候,苹果会检验这个 hash 值,当苹果监测到这个动态库非法时,就会造成 Crash
iOS 如何使用 framework 来进行动态更新!
重要参考文档(一定要看):
谈谈 Mach-O
Mach-O
- 在制作 framework 的时候需要选择这个 Mach-O Type.
- 为Mach Object文件格式的缩写,它是一种用于可执行文件,目标代码,动态库,内核转储的文件格式。作为a.out格式的替代,Mach-O提供了更强的扩展性,并提升了符号表中信息的访问速度。