zoukankan      html  css  js  c++  java
  • iOS中的【库】

    理论篇:

      动态库静态库

      动态库与静态库都是编译好的二进制文件,其用法不同;

      对于静态库而言,只在链接过程中起作用;静态库是target object文件(.o MACH-O)的archive结构,链接时会将各LC、Section、Symbol Table、String Table等合并生成最终可执行文件的MACH-O结构,机器码被merge在__TEXT段,MACH-O按Segment和Setion不同,被映射到VM的不同Page中。

    • 在 build 的过程中只会参与链接的过程,而这个链接的过程简单的讲就是合并,并且链接器只会将静态库中被使用的部分合并到可执行文件中去。相比较于动态库,静态库的处理起来要简单的多,具体如下图:
    11437742-49946939660dd0f6
      静态库链接过程
    • 链接器会将所有.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 等通过代码或者命令的方式来加载。【在程序启动之后】
    • 但是不论是哪种动态库,相比较与静态库,动态库处理起来要棘手的多。由于动态库是动态的,所以你事先不知道某个函数的具体地址。因此动态链接器在链接函数的时候需要做大量的工作。
    • 而实现这个动态链接是使用了 Procedure Linkage Table (PLT)。首先这个 PLT 列出了程序中每一个函数的调用,当程序开始运行,如果动态库被加载到内存中,PLT 会去寻找动态的地址并记录下来,如果每个函数都被调用过的话,下一次调用就可以通过 PLT 直接跳转了,但是和静态库还是有点区别的是,每一个函数的调用还是需要通过一张 PLT。这也正是所有静态链接做的事情都搬到运行时来做了,会导致更慢 的原因。

      

    动态库到底在内存哪块区域的问题

    • 其实这个命题最开始就跑偏了,在和@酷酷的哀殿 等几个小伙伴讨论未果之后,在老司机@Casa Taloyum 大神 的点拨下,明白了问题出在了哪里。
    • 首先,不管是静态库、动态库,两者的区别和在内存哪个区域没有关系,最本质的区别是,一个的函数调用等在编译时候就已经确定,而动态库是动态加载的。换句话说,静态库修改了东西,整个程序需要重新编译,而对于动态库的修改而言,只需要重启 app(重置 PLT )。
    • 至于在内存的哪个区域,和是 静态库 or 动态库 没有关系。代码段、数据段这些,都是程序加载时就进入的。堆一般是文件buffer分配、对象初始化等时候用的。栈是函数出入口指针,局部常规变量用的。只要 malloc 都在堆里。具体的可以参照这里
    • 还需要提一下的是,如果是动态加载库,那么在没有加载的时候,代码段、数据段这些也是不会加载进去的。lazy load。

      .framework  与  .a

    • .a是一个纯二进制文件,不能直接拿来使用,需要配合头文件、资源文件一起使用。在 iOS 中是作为静态库的文件名后缀。
    • .framework中除了有二进制文件之外还有资源文件,可以拿来直接使用。
    • 在不能开发动态库的时候,其实 『.framework = .a + .h + bundle』。而当 Xcode 6 出来以后,我们可以开发动态库后『.framework = 静态库/动态库 + .h + bundle』

      .tbd 与 .dylib

    • 对于静态库的后缀名是.a,那么动态库的后缀名是什么呢?
    • 可以从 libsqlite3.dylib 这里我们可以知道 .dylib 就是动态库的文件的后缀名。
    • 那么 .tbd 又是什么东西呢?其实,细心的朋友都早已发现了从 Xcode7 我们再导入系统提供的动态库的时候,不再有.dylib了,取而代之的是.tbd。而 .tbd 其实是一个YAML本文文件,描述了需要链接的动态库的信息。主要目的是为了减少app 的下载大小。

      小总结

    • 首先,相比较与静态库和动态库,动态库在包体积、启动时间还有内存占比上都是很有优势的。
    • 为了解决 .a 的文件不能直接用,还要配备 .h 和资源文件,苹果推出了一个叫做 .framework 的东西,而且还支持动态库。

    内存的五大分区

    • BSS段:
      • BSS段( bss segment )通常是指用来存放程序中未初始化的全局变量和静态变量的一块内存区域。
      • 这里注意一个问题:一般的书上都会说全局变量和静态变量是会自动初始化的,那么哪来的未初始化的变量呢?变量的初始化可以分为显示初始化和隐式初始化,全局变量和静态变量如果程序员自己不初始化的话的确也会被初始化,那就是不管什么类型都初始化为0,这种没有显示初始化的就 是我们这里所说的未初始化。既然都是0那么就没必要把每个0都存储起来,从而节省磁盘空间,这是BSS的主要作用
      • BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。 BSS节不包含任何数据,只是简单的维护开始和结束的地址,即总大小。以便内存区能在运行时分配并被有效地清零。BSS节在应用程序的二进制映象文件中并不存在,即不占用 磁盘空间 而只在运行的时候占用内存空间 ,所以如果全局变量和静态变量未初始化那么其可执行文件要小很多
    • 数据段(data segment)
      • 通常是指用来存放程序中已经初始化的全局变量和静态变量的一块内存区域。数据段属于静态内存分配,可以分为只读数据段读写数据段。字符串常量等,但一般都是放在只读数据段中。
    • 代码段(code segment/text segment)
      • 通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等,但一般都是放在只读数据段中 。
    • 堆(heap)
      • 堆是用于存放进程运行中被动态分配的内存段,它的大小并不固定,可动态扩张或 缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张); 当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)
    • 栈 (stack heap)
      • 栈又称堆栈, 是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}” 中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外, 在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值 也会被存放回栈中。由于栈的先进先出特点,所以 栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。

    13437742-aa9178aa3632ea2a
    内存分区

    小 Tips

    • 栈区中的变量不需要程序员管理
    • 堆区的需要程序员管理
    • 在 iOS 中堆区的内存是所有应用程序共享
    • 系统使用表级别结构来分配内存空间,所以逻辑地址和物理地址可能不一样

      

    从源代码到app

    • 预处理(Pre-process):把宏替换,删除注释,展开头文件,产生 .i 文件。
    • 编译(Compliling):把之前的 .i 文件转换成汇编语言,产生 .s文件。
    • 汇编(Asembly):把汇编语言文件转换为机器码文件,产生 .o 文件。
    • 链接(Link):对.o文件中的对于其他的库的引用的地方进行引用,生成最后的可执行文件(同时也包括多个 .o 文件进行 link)。

    ld && libtool

    • ld :用于产生可执行文件。
    • libtool:产生 lib 的工具。

    Build phases && Build rules && Build settings

    • Build phases: 主要是用来控制从源文件到可执行文件的整个过程的,所以应该说是面向源文件的,包括编译哪些文件,以及在编译过程中执行一些自定义的脚本什么的。
    • Build rules: 主要是用来控制如何编译某种类型的源文件的,假如说相对某种类型的原文件进行特定的编译,那么就应该在这里进行编辑了。同时这里也会大量的运用一些 xcode 中的环境变量,完整的官方文档在这里:Build Settings Reference
    • Build settings:则是对编译工作的细节进行设定,在这个窗口里可以看见大量的设置选项,从编译到打包再到代码签名都有,这里要注意 settings 的 section 分类,同时一般通过右侧的 inspector 就可以很好的理解选项的意义了。

    谈谈 Mach-O

    14437742-2b782afef864661e
    Mach-O
    • 在制作 framework 的时候需要选择这个 Mach-O Type.
    • 为Mach Object文件格式的缩写,它是一种用于可执行文件,目标代码,动态库,内核转储的文件格式。作为a.out格式的替代,Mach-O提供了更强的扩展性,并提升了符号表中信息的访问速度。
  • 相关阅读:
    spring data jpa @index无法创建索引
    vue form绑定数据后只能显示不能编辑
    vue dialog每次打开会展示上一次数据(转载)
    vue-i18n突然所有的都解析不出来了
    sass计算高度
    动态指定日志路径(logback)
    Springboot配置文件内容加密
    java环境变量修改后不生效
    SOAP和REST
    Git配置别名
  • 原文地址:https://www.cnblogs.com/zhangrunchao/p/5969922.html
Copyright © 2011-2022 走看看