-参考文章1:从预编译的角度理解Swift与Objective-C及混编机制
目录
modulemap
一、介绍
modulemap 是为解决 Objective-C 头文件引用问题引入的。解决了头文件的重复编译问题(常规引用头文件会重复编译),并且使头文件可以按需引入(PCH是全量引入)。
将非模块化的 Objective-C 代码转化为模块化,使其可以以模块的方式被其它(OC或Swift)模块导入。
说明:modulemap 文件在编译阶段发挥作用,当编译链接成可执行文件后就没有用了
目前知道的是 modulemap 应用在Framework内,当我们创建一个TestFramework里面有Object-C代码时,我们就可以配置modulemap使其可以以模块的方式被其它(OC或Swift)模块导入,并使用TestFramework内的OC代码。
打开配置modulemap的开关(默认是打开的),Defines Module 就会自动生成modulemap文件。
我们看下产物目录下有个modules目录
二、引用
1、在app的Swift引用
如果需要将 TestFramework 内的 Objective-C 代码暴露给 Swift 使用,必须配置 modulemap 文件。
// swift中引用
import TestFramework
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let vc = TestViewController();
self.navigationController?.pushViewController(vc, animated: false);
}
2、在app的Objec-C引用
通过modulemap的引用方式
// 在OC代码中引用
@import TestFramwork;
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
TestViewController *testVC = [[TestViewController alloc] init];
[self.navigationController pushViewController:testVC animated:NO];
}
也可以使用传统的头文件引用方式,
// 如果配置了modulemap,头文件引用方式也会被编译器自动转为modulemap引用。
// 如果没有配置modulemap,会按传统方式搜索头文件引用。
#import <TestFramwork/TestViewController.h>
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event {
TestViewController *testVC = [[TestViewController alloc] init];
[self.navigationController pushViewController:testVC animated:NO];
}
3、关闭Defines Module 选项
我们将TestFramework的Defines Module 选项关闭。
首先我们会发现编译产物中没有生成modulemap文件了。
然后我们发现在Swift中的引用和在OC代码中采用模块引用的方式都报错了。
Module 'TestFramwork' not found
证明modulemap确实不再生成了。
但OC代码的下面这种引用方式还是可以正常编译的,因为关闭module后它会以搜索头文件的方式来查找。
#import <TestFramwork/TestViewController.h>
我们按照图示进行操作后,查看预编译的文件,会发下下面代码
@end
我们打开 defines module 选项,然后再进行预编译查看,会发现下面代码
/* clang -E: implicit import for #import <TestFramwork/TestViewController.h> */

swiftmodule
一、介绍
xx.swiftmodule 文件是 Swift 编译器生成的文件,描述了 Swift 模块的公共接口。它包含了 Swift 模块的元数据,是 Swift 模块化的核心之一。
说明:不管是app target还是framework,只要有swift代码就会生成对应的xx.swiftmodule文件
我们在TestFramework内添加一个SwiftViewController.swift文件,编译后观察产物目录。


发现app target内有df_ios.swiftmodule文件夹,在 TestFramework内有TestFramework.swiftmodule 文件。
并且还注意到TestFramework内既有OC代码又有Swift代码时,Modules目录下会同时有module.modulemap文件和TestFramework.swiftmodule。
二、引用
1、在app的Swift中引用
// 引用模块
import TestFramwork
// 使用代码
let vc = SwiftViewController()
self.navigationController?.pushViewController(vc, animated: true)
app中的swift代码不管是引用framework内的oc代码还是swift代码,都是通过模块的方式来引用。
如果关闭framework的defines module选项,那么swift引用oc代码就会失败
2、在app的OC中引用
通过modulemap的引用方式
@import TestFramework;
SwiftViewController *vc = [[SwiftViewController alloc] init];
[self.navigationController pushViewController:vc animated:YES];
头文件的方式引用
#import <TestFramwork/TestFramwork-Swift.h>
SwiftViewController *vc = [[SwiftViewController alloc] init];
[self.navigationController pushViewController:vc animated:YES];
如果modulemap和头文件的引用方式报错:找不到SwiftViewController,打开TestFramework下面的开关就行
总结
1、Modules/module.modulemap是oc代码模块化的元数据,要想以模块的方式引用oc代码,就必须配置modulemap文件。
2、Modules/TestFramework.swiftmodule是swift代码模块化的元数据,只要有swift代码就会生成对应的xx.swiftmodule文件。
3、TestFramwork-Swift.h是在TestFramework.swiftmodule的基础上生成的,包含了所有被@objc修饰的类和方法。这里需要说明的是oc即使采用@import TestFramework;
模块引用的方式也是通过TestFramwork-Swift.h
找到swift的代码声明的,我们从下面的内容可以看到module.modulemap文件内是有TestFramwork-Swift.h
的相关信息的。
framework module TestFramwork {
umbrella header "TestFramwork.h"
export *
module * { export * }
}
module TestFramwork.Swift {
header "TestFramwork-Swift.h"
requires objc
}
行者常至,为者常成!