module - ShenYj/ShenYj.github.io GitHub Wiki

Module(模块)

简介

一个 module 是机器代码和数据的最小单位,可以独立于其他代码单位进行连接。

Moduleclang 提供的一种处理头文件的解析格式

通常 module 是通过编译单个源文件生成的目标文件。例如,当前 test.m被编译成目标文件 test.o时,目标文件就代表了一个 module, 但是有一个问题, module 在调用的时候会产生开销,比如我们在使用一个静态库的时候。

Object-C 为例,默认情况下,在 .m 中会进行 import 头文件,也就代表着头文件要参与编译,一次次的编译注定会影响到编译的时长

默认 Frameowrk 开发不会给自动创建 .modulemap 文件,再试在编译后会自动生成,我可以可以在编写 framework 的时候就直接自己创建,并在 build setting - Module Map File 中配置路径

探索

目录结构

.
├── prebuild
└── test
    ├── A.h
    ├── B.h
    ├── module.modulemap
    └── use.c

prebuild 是一个空文件夹,存放生成物

代码

获取演示文件

  • A.h

    /* A.h */
    #ifdef ENABLE_A
    void a() {}
    #endif
  • B.h

    B.h 中导入了 A.h

    /* B.h */
    #import "A.h"
  • Use.c

    Use.c 中导入了 B.h

    /* use.c */
    #import "B.h"
    void use() {
    #ifdef ENABLE_A
    a();
    #endif
    }
  • module.modulemap

    /* module.modulemap */
    
    module A {
    header "A.h"
    }
    
    module B {
    header "B.h"
    export A
    }

    描述头文件与 module 之间的关系, Module A 代表 A.h
    将在 B 中将 A 暴露,重新导入导出,一般使用 export * 通配

通过 clang 编译链接成 .o文件,同时生成 module

clang -fmodules -fmodule-map-file=module.modulemap -fmodules-cache-path=../prebuilt -c use.c -o use.o
  • -fmodules:允许使用module语言来表示头文件
  • -fmodule-map-file:module map的路径。如不指明默认module.modulemap
  • -fmodules-cache-path:编译后的module缓存路径

prebuild 中的产物

.
├── 1B079OJNMTTUL
│   ├── A-1TFUZYW6I2HVV.pcm
│   ├── B-1TFUZYW6I2HVV.pcm
│   └── modules.idx
└── modules.timestamp

.pcm 就是头文件预先编译后的产物
通过 Module会先 .h 头文件预先编译成二进制,在系统目录下缓存
在所有 .m中再次使用的时候,直接取二进制来用即可

分析AFN的.modulemap文件

  • AFN

    framework module AFNetworking {
    umbrella header "AFNetworking-umbrella.h"
    
    export *
    module * { export * }
    }
    • framework module AFNetworking: 声明一个 framework module, 名称是 AFNetworking
    • umbrella header "AFNetworking-umbrella.h": 指定文件 AFNetworking-umbrella.h代表伞柄,文件中的所有头文件作为伞骨
      • umbrella <目录>: 伞柄, 目录下面所有的 .h 文件作为伞骨
      • header: 指定一个文件
    • export *: 重新导出
    • module * { export * }: 创建了一个子 module -> Module *,再重新导出 -> { export * }
      • 如果需要显示指明子module名称,使用关键字 explicit,通配就是头文件叫啥,子模块就叫啥
        • e.g.: explicit module XXXXXX
      • 使用了子module,在配合@import的时候就可以通过 module.submodule来导入使用了

导入方式

  • #include
    传统的 #include 的方式: 一个头文件被多少个代码文件导入,那么就会参与多少次的编译
  • #import
    #include 的基础上做了重复引用处理
  • @import
    配合 Module 使用, 在引入的时候还可以显式的指定引用模块中的某个子模块, 比如 @import UIKit.UIWindow

Module 功能默认开启,在开启 Module 功能后,三种写法都是可以的

.private.modulemap

通过 Module 可以很方便的来导入我们所依赖的库模块及子模块,默认情况下所有的模块和子模块均是对外暴露的, Module也考虑到了这一点, 提供了一种暴露方式控制的配置方法

⚠️ **GitHub.com Fallback** ⚠️