一、module与modulemap的作用
1、 module
- Module(模块)-最小的代码单元。
一个Module是机器代码和数据的最小单位,可以独立于其他代码单位进行链接。通常,Module是通过编译单个源文件生成的目标文件。例如,当前的test.m被编译成目标文件test.o时,当前的目标文件就代表了一个Module。但是,有一个问题,Module在调用的时候会产生开销,比如我们在使用一个静态库的时候,可以这样使用。
// -fmodules:允许使用module语言来表示头文件
// -fmodule-map-file:module map的路径。如不指明默认module.modulemap
// -fmodules-cache-path:编译后的module缓存路径
clang -fmodules -fmodule-map-file=Cat.modulemap -fmodules-cache-path=../prebuilt -c use.c -o use.o
2、modulemap
- modulemap定义:用来描述头文件与module之间映射的关系的文件。
// AFNetworking的module.modulemap文件
// framework module 名称 AFNetworking
framework module AFNetworking {
// umbrella <目录> 伞柄 <目录>/.h
// AFNetworking-umbrella.h 伞柄 AFNetworking-umbrella.h/.h 伞骨
umbrella header "AFNetworking-umbrella.h"
// 重新导出
export *
// module: 子module*
module * { export * }
}
/*
module:定义一个module
export:导出当前代表的头文件使用的头文件
export * :匹配目录下所有的头文件
module * :目录下所有的头文件都当作一个子module
explicit :显式声明一个子module的名称
*/
3、module实操
默认开启module之后,在引入头文件时使用include""、 import<>、@import ;这三种写法,最终都会被转化成@import。
-
@import TestStaticFramework;
这个静态库中可能包含了许多的.o文件。岂不是要导入很多的Module。并不需要。在静态链接的时候,也就是静态库链接到主项目或者动态库时,最后生成可执行文件或者动态库时。静态链接器可以把多个Module链接优化成一个,来减少本来多个Module直接调用的问题。 -
每次包含标头时,编译器都必须可传递地预处理和解析该标头及其包含的每个标头中的文本。必须对应用程序中的每个翻译单元重复此过程,这涉及大量的冗余工作。
-
在具有N个翻译单元和每个翻译单元中包含M个标头的项目中,即使M个标头中的大多数在多个翻译单元之间共享,编译器仍在执行M x N个工作。
-
include伪指令被预处理程序视为文本包含,因此在包含时必须接受任何活动的宏定义。如果任何活动宏定义碰巧与库中的名称冲突,则可能会破坏库API或导致库头本身的编译失败。
-
此外,导入模块时将自动提供使用该模块所需的任何链接器标志
-
std.io模块仅编译一次,并且将模块导入转换单元是恒定时间操作(与模块系统无关)。因此,每个软件库的API仅解析一次,从而将M x N编译问题减少为M + N问题。
-
每个模块都被解析为一个独立的实体,因此它具有一致的预处理器环境。
-
此外,在遇到导入声明时,当前的预处理器定义将被忽略,
-
@import上面的声明导入std模块的全部内容(其中将包含例如整个C或C ++标准库),并在当前翻译单元中提供其API。要仅导入模块的一部分,可以使用点语法来特定特定的子模块
-
模块会自动将#include指令转换为相应的模块导入
APINotes官方链接:https://clang.llvm.org/docs/APINotes.html
Modules官方链接:https://clang.llvm.org/docs/Modules.html#export-declaration
二、Swift库使用OC代码
Swift库使用OC代码:不能使用桥接文件
以下方式就可以使用了:1. oc的头文件放到modulemap下;2. oc的头文件放到私有的modulemap下; 3. oc语言特性 协议 -》swift-〉协议(暴露) -》 OC
- 创建是私有的modulemap文件命令中必须含有 private 比如:LGSwiftFramework.private.modulemap。并且在该文件内部LGSwiftFramework后面必须加上_Private,其中 P 必须大写。这些都是规则。
- private.modulemap文件并不是真正意义上的让外部文件不能使用期私有的module,而是仅仅做了一个标识,来区分与.modulemap文件的不同而已。
三、Swift头文件
- 在 Xcode 9 之后,Swift 开始⽀持静态库。
Swift 没有头⽂件的概念,那么我们外界要使⽤Swift中⽤public修饰的类和函数怎么办?
Swift库中引⼊了⼀个全新的⽂件.swiftmodule。
.swiftmodule 包含序列化过的 AST(抽象语法树,Abstract Syntax Tree),也包含 SIL(Swift 中间语⾔,Swift Intermediate Language)。
四、Swift静态库合并
-
cp -Rv -- "${BUILT_PRODUCTS_DIR}/" "${SOURCE_ROOT}/../Products"
复制命令:cp(复制) -R(单独复制文件)-v(使cp冗长,显示文件的原样)
$(BUILT_PRODUCTS_DIR):build成功后的,最终产品路径可以在Build Settings参数的Per-configuration Build Products Path项里设置
$(TARGET_NAME):目标工程名称
$(SRCROOT):工程文件(比如Nuno.xcodeproj)的路径
$(CURRENT_PROJECT_VERSION):当前工程版本号
${SOURCE_ROOT}:项目根目录(如果和项目根目录平级或更高,就$(SOURCE_ROOT)/../../IBAForms/headers) -
libtool -staticlibtool -static LGSwiftA LGSwiftB -o libLGSwiftC.a -o libLGSwiftC.a
将 LGSwiftA 和 LGSwiftB 合并为libLGSwiftC.a文件
报出的警告:合并的内容中LGSwiftTeacher.o文件重复,这里只是报警告,不会报错。libtool 合并静态库本身,合并时会提示存在相同的文件,且不进行合并。
警告 -
ar -t libLGSwiftC.a 列出libLGSwiftC.a文件里面包含的文件。
列出静态库文件内容 -
合并 Swift 静态库难题:静态库合并后还有 .swiftmodule文件没有合并,所以将用到的头文件和Swift头文件和modulemap文件通过目录的形式放到一起。如下图:
header
swiftmodule -
Swift 合并的静态库文件应用,进行配置
OC要用合并的静态库:clang: other c flags :-fmodule-map-file <modulemap path>
Swift要用合并的静态库 : SwiftC :other swift flags 显式告诉SwiftC <modulemap dir>
HEADER_SEARCH_PATHS = $(inherited) "${SRCROOT}/LGSwiftC/Public/LGSwiftA.framework/Headers" "${SRCROOT}/LGSwiftC/Public/LGSwiftB.framework/Headers"
// OTHER_CFLAGS:传递给用来编译C或者OC的编译器,当前就是clang
OTHER_CFLAGS="-fmodule-map-file=${SRCROOT}/LGSwiftC/Public/LGSwiftA.framework/module.modulemap" "-fmodule-map-file=${SRCROOT}/LGSwiftC/Public/LGSwiftB.framework/module.modulemap"
// SWIFT_INCLUDE_PATHS: 传递给SwiftC编译器,告诉他去下面的路径中查找module.file
SWIFT_INCLUDE_PATHS="${SRCROOT}/LGSwiftC/Public/LGSwiftB.framework" "${SRCROOT}/LGSwiftC/Public/LGSwiftA.framework"
五、Swift与OC相互映射
- OC映射到Swift方式:1. 宏;2. <工程名称>.apinotes。
- 宏配置的缺点:如果一个 SDK 使用 OC 来写的,现在需要适配 Swift。这样就需要给每一个方法或属性添加宏来适配,这样就会导致有大量工作要做,费时费力。并且要修改原有代码。
- .apinotes:文件以.apinotes结尾,且该文件一定要放在 SDK 的目录里。该文件是采用yaml格式书写。官方地址:https://clang.llvm.org/docs/APINotes.html
---
Name: OCFramework
Classes:
- Name: LGToSwift
SwiftName: ToSwift
Methods:
- Selector: "changeTeacherName:"
Parameters:
- Position: 0
Nullability: O
MethodKind: Instance
SwiftPrivate: true
Availability: nonswift
AvailabilityMsg: "这个不能用"
- Selector: "initWithName:"
MethodKind: Instance
DesignatedInit: true
网友评论