JHHK

欢迎来到我的个人网站
行者常至 为者常成

Swift混编2

-参考文章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>
关闭 Defines Module 我们来验证下:
我们按照图示进行操作后,查看预编译的文件,会发下下面代码
@interface TestViewController : UIViewController
@end
证明是以头文件的方式查找并进行替换的。

我们打开 defines module 选项,然后再进行预编译查看,会发现下面代码
#pragma clang module import TestFramwork.TestViewController
/* clang -E: implicit import for #import <TestFramwork/TestViewController.h> */
证明是以moduel的方式引入的。

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
}

行者常至,为者常成!





R
Valine - A simple comment system based on Leancloud.