iOS 原生模块
原生模块和原生组件是我们稳定技术,由旧架构使用。当新架构稳定后,它们将被弃用。新架构使用 Turbo 原生模块 和 Fabric 原生组件 来实现类似的结果。
欢迎来到 iOS 的原生模块。请先阅读 原生模块简介,了解原生模块是什么。
创建日历原生模块
在以下指南中,您将创建一个名为 CalendarModule
的原生模块,它允许您从 JavaScript 访问 Apple 的日历 API。最后,您将能够从 JavaScript 调用 CalendarModule.createCalendarEvent('Dinner Party', 'My House');
,调用创建日历事件的原生方法。
设置
要开始,请在 Xcode 中打开 React Native 应用程序中的 iOS 项目。您可以在 React Native 应用程序中找到您的 iOS 项目
我们建议使用 Xcode 编写您的原生代码。Xcode 是为 iOS 开发而构建的,使用它将帮助您快速解决代码语法等较小的错误。
创建自定义原生模块文件
第一步是创建我们的主要自定义原生模块头文件和实现文件。创建一个名为RCTCalendarModule.h
的新文件
并添加以下内容
// RCTCalendarModule.h
#import <React/RCTBridgeModule.h>
@interface RCTCalendarModule : NSObject <RCTBridgeModule>
@end
您可以使用任何适合您正在构建的原生模块的名称。将类命名为RCTCalendarModule
,因为您正在创建一个日历原生模块。由于ObjC没有像Java或C++那样的语言级命名空间支持,所以惯例是在类名前面加上一个子字符串。这可能是您的应用程序名称或基础设施名称的缩写。在本例中,RCT指的是React。
如下所示,CalendarModule类实现了RCTBridgeModule
协议。原生模块是一个实现RCTBridgeModule
协议的Objective-C类。
接下来,让我们开始实现原生模块。在同一个文件夹中创建相应的实现文件RCTCalendarModule.m
,并包含以下内容
// RCTCalendarModule.m
#import "RCTCalendarModule.h"
@implementation RCTCalendarModule
// To export a module named RCTCalendarModule
RCT_EXPORT_MODULE();
@end
模块名称
目前,您的RCTCalendarModule.m
原生模块只包含一个RCT_EXPORT_MODULE
宏,该宏将原生模块类导出并注册到React Native。RCT_EXPORT_MODULE
宏还接受一个可选参数,该参数指定模块在您的JavaScript代码中可访问的名称。
此参数不是字符串文字。在下面的示例中,传递了RCT_EXPORT_MODULE(CalendarModuleFoo)
,而不是RCT_EXPORT_MODULE("CalendarModuleFoo")
。
// To export a module named CalendarModuleFoo
RCT_EXPORT_MODULE(CalendarModuleFoo);
然后可以在JS中访问原生模块,如下所示
const {CalendarModuleFoo} = ReactNative.NativeModules;
如果您没有指定名称,JavaScript模块名称将与Objective-C类名称匹配,并删除任何“RCT”或“RK”前缀。
让我们参考下面的例子,在调用 `RCT_EXPORT_MODULE` 时不带任何参数。这样,模块将使用 `CalendarModule` 这个名字暴露给 React Native,因为这是 Objective-C 类名,去掉了 RCT 部分。
// Without passing in a name this will export the native module name as the Objective-C class name with “RCT” removed
RCT_EXPORT_MODULE();
然后可以在JS中访问原生模块,如下所示
const {CalendarModule} = ReactNative.NativeModules;
将原生方法导出到 JavaScript
除非明确告知,否则 React Native 不会将原生模块中的任何方法暴露给 JavaScript。可以使用 `RCT_EXPORT_METHOD` 宏来实现。在 `RCT_EXPORT_METHOD` 宏中编写的函数是异步的,因此返回值始终为 void。为了将 `RCT_EXPORT_METHOD` 函数的结果传递给 JavaScript,可以使用回调或发出事件(将在下面介绍)。让我们使用 `RCT_EXPORT_METHOD` 宏为我们的 `CalendarModule` 原生模块设置一个原生函数。将其命名为 `createCalendarEvent()`,目前让它接收 name 和 location 参数作为字符串。参数类型选项将在稍后介绍。
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)name location:(NSString *)location)
{
}
请注意,除非你的函数依赖于 RCT 参数转换(见下面的参数类型),否则 `RCT_EXPORT_METHOD` 宏在使用 TurboModules 时将不再需要。最终,React Native 会移除 `RCT_EXPORT_MACRO`,因此我们不建议使用 `RCTConvert`。相反,你可以在函数体中进行参数转换。
在你构建 `createCalendarEvent()` 函数的功能之前,在函数中添加一个控制台日志,以便你确认它已从 React Native 应用程序中的 JavaScript 调用。使用 React 中的 `RCTLog` API。让我们在文件顶部导入该头文件,然后添加日志调用。
#import <React/RCTLog.h>
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)name location:(NSString *)location)
{
RCTLogInfo(@"Pretending to create an event %@ at %@", name, location);
}
同步方法
你可以使用 `RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD` 创建一个同步原生函数。
RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD(getName)
{
return [[UIDevice currentDevice] name];
}
此函数的返回值必须为对象类型(id),并且应该可以序列化为 JSON。这意味着该钩子只能返回 nil 或 JSON 值(例如 NSNumber、NSString、NSArray、NSDictionary)。
目前,我们不建议使用同步方法,因为同步调用函数可能会导致严重的性能损失,并给你的原生模块引入线程相关的错误。此外,请注意,如果你选择使用 `RCT_EXPORT_BLOCKING_SYNCHRONOUS_METHOD`,你的应用程序将无法再使用 Google Chrome 调试器。这是因为同步方法需要 JS VM 与应用程序共享内存。对于 Google Chrome 调试器,React Native 在 Google Chrome 中的 JS VM 内运行,并通过 WebSockets 与移动设备进行异步通信。
测试你构建的内容
此时,你已在 iOS 中为你的原生模块设置了基本脚手架。通过访问原生模块并在 JavaScript 中调用其导出的方法来测试它。
在你的应用程序中找到一个你想添加对原生模块的 createCalendarEvent()
方法的调用的位置。以下是一个示例组件 NewModuleButton
,你可以将其添加到你的应用程序中。你可以在 NewModuleButton
的 onPress()
函数中调用原生模块。
import React from 'react';
import {NativeModules, Button} from 'react-native';
const NewModuleButton = () => {
const onPress = () => {
console.log('We will invoke the native module here!');
};
return (
<Button
title="Click to invoke your native module!"
color="#841584"
onPress={onPress}
/>
);
};
export default NewModuleButton;
为了从 JavaScript 访问你的原生模块,你需要先从 React Native 导入 NativeModules
import {NativeModules} from 'react-native';
然后,你可以从 NativeModules
中访问 CalendarModule
原生模块。
const {CalendarModule} = NativeModules;
现在你已经可以使用 CalendarModule
原生模块了,你可以调用你的原生方法 createCalendarEvent()
。下面将其添加到 NewModuleButton
中的 onPress()
方法中
const onPress = () => {
CalendarModule.createCalendarEvent('testName', 'testLocation');
};
最后一步是重建 React Native 应用程序,以便你可以使用最新的原生代码(包含你的新原生模块!)。在你的命令行中,在 React Native 应用程序所在的目录中,运行以下命令
- npm
- Yarn
npm run ios
yarn ios
边迭代边构建
当你完成这些指南并迭代你的原生模块时,你需要对你的应用程序进行原生重建,以便从 JavaScript 访问你最新的更改。这是因为你正在编写的代码位于应用程序的原生部分。虽然 React Native 的 metro 捆绑器可以监视 JavaScript 中的更改并为你动态重建 JS 捆绑包,但它不会对原生代码执行此操作。因此,如果你想测试你最新的原生更改,你需要使用上面的命令进行重建。
回顾✨
你现在应该能够在 JavaScript 中调用你的原生模块上的 createCalendarEvent()
方法。由于你在函数中使用了 RCTLog
,你可以通过 在你的应用程序中启用调试模式 并查看 Chrome 中的 JS 控制台或移动应用程序调试器 Flipper 来确认你的原生方法是否被调用。你应该在每次调用原生模块方法时看到你的 RCTLogInfo(@"Pretending to create an event %@ at %@", name, location);
消息。
此时,您已创建了一个 iOS 原生模块,并从 React Native 应用程序中的 JavaScript 代码调用了该模块的方法。您可以继续阅读以了解有关以下内容的更多信息:您的原生模块方法接受哪些参数类型,以及如何在原生模块中设置回调和 Promise。
超越日历原生模块
更好的原生模块导出
通过从 NativeModules
中提取原生模块来导入它,就像上面那样,有点笨拙。
为了让您的原生模块的使用者不必每次想要访问您的原生模块时都这样做,您可以为该模块创建一个 JavaScript 包装器。创建一个名为 NativeCalendarModule.js 的新 JavaScript 文件,内容如下
/**
* This exposes the native CalendarModule module as a JS module. This has a
* function 'createCalendarEvent' which takes the following parameters:
* 1. String name: A string representing the name of the event
* 2. String location: A string representing the location of the event
*/
import {NativeModules} from 'react-native';
const {CalendarModule} = NativeModules;
export default CalendarModule;
此 JavaScript 文件也成为您添加任何 JavaScript 侧功能的理想位置。例如,如果您使用像 TypeScript 这样的类型系统,您可以在此处为您的原生模块添加类型注释。虽然 React Native 尚未支持 Native 到 JS 的类型安全,但使用这些类型注释,您所有的 JS 代码都将是类型安全的。这些注释还将使您更容易切换到类型安全的原生模块。以下是为日历模块添加类型安全的示例
/**
* This exposes the native CalendarModule module as a JS module. This has a
* function 'createCalendarEvent' which takes the following parameters:
*
* 1. String name: A string representing the name of the event
* 2. String location: A string representing the location of the event
*/
import {NativeModules} from 'react-native';
const {CalendarModule} = NativeModules;
interface CalendarInterface {
createCalendarEvent(name: string, location: string): void;
}
export default CalendarModule as CalendarInterface;
在您的其他 JavaScript 文件中,您可以像这样访问原生模块并调用其方法
import NativeCalendarModule from './NativeCalendarModule';
NativeCalendarModule.createCalendarEvent('foo', 'bar');
请注意,这假设您导入
CalendarModule
的位置与NativeCalendarModule.js
位于同一层次结构中。请根据需要更新相对导入。
参数类型
当在 JavaScript 中调用原生模块方法时,React Native 会将参数从 JS 对象转换为它们的 Objective-C/Swift 对象模拟。例如,如果您的 Objective-C 原生模块方法接受一个 NSNumber,那么在 JS 中您需要使用一个数字调用该方法。React Native 会为您处理转换。以下是原生模块方法支持的参数类型及其映射到的 JavaScript 等效类型的列表。
Objective-C | JavaScript |
---|---|
NSString | 字符串, ?字符串 |
布尔值 | 布尔值 |
双精度浮点数 | 数字 |
NSNumber | ?数字 |
NSArray | 数组, ?数组 |
NSDictionary | 对象, ?对象 |
RCTResponseSenderBlock | 函数 (成功) |
RCTResponseSenderBlock, RCTResponseErrorBlock | 函数 (失败) |
RCTPromiseResolveBlock, RCTPromiseRejectBlock | Promise |
以下类型目前支持,但将在 TurboModules 中不再支持。请避免使用它们。
- 函数 (失败) -> RCTResponseErrorBlock
- 数字 -> NSInteger
- 数字 -> CGFloat
- 数字 -> float
对于 iOS,您还可以使用 RCTConvert
类支持的任何参数类型编写原生模块方法(有关支持内容的详细信息,请参阅 RCTConvert)。RCTConvert 辅助函数都接受 JSON 值作为输入,并将其映射到原生 Objective-C 类型或类。
导出常量
原生模块可以通过覆盖原生方法 constantsToExport()
来导出常量。下面 constantsToExport()
被覆盖,并返回一个字典,其中包含一个默认事件名称属性,您可以在 JavaScript 中像这样访问它
- (NSDictionary *)constantsToExport
{
return @{ @"DEFAULT_EVENT_NAME": @"New Event" };
}
然后可以通过在 JS 中调用原生模块上的 getConstants()
来访问该常量,如下所示
const {DEFAULT_EVENT_NAME} = CalendarModule.getConstants();
console.log(DEFAULT_EVENT_NAME);
从技术上讲,可以从 NativeModule
对象直接访问在 constantsToExport()
中导出的常量。这在 TurboModules 中将不再支持,因此我们鼓励社区切换到上述方法,以避免将来需要迁移。
请注意,常量仅在初始化时导出,因此如果您在运行时更改
constantsToExport()
值,它不会影响 JavaScript 环境。
对于 iOS,如果您覆盖 constantsToExport()
,那么您还应该实现 + requiresMainQueueSetup
,以让 React Native 知道您的模块是否需要在主线程上初始化,在任何 JavaScript 代码执行之前。否则,您将看到一个警告,提示将来您的模块可能会在后台线程上初始化,除非您使用 + requiresMainQueueSetup:
明确选择退出。如果您的模块不需要访问 UIKit,那么您应该对 + requiresMainQueueSetup
响应 NO。
回调
原生模块还支持一种独特的参数类型 - 回调。回调用于将数据从 Objective-C 传递到 JavaScript 以用于异步方法。它们还可以用于从原生端异步执行 JS。
对于 iOS,回调使用类型 RCTResponseSenderBlock
实现。在下面,回调参数 myCallBack
被添加到 createCalendarEventMethod()
中。
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)title
location:(NSString *)location
myCallback:(RCTResponseSenderBlock)callback)
然后,您可以在本机函数中调用回调,并提供您想要传递给 JavaScript 的任何结果,这些结果都包含在一个数组中。请注意,RCTResponseSenderBlock
仅接受一个参数 - 一个要传递给 JavaScript 回调的参数数组。在下面,您将传递回在之前调用中创建的事件的 ID。
重要的是要注意,回调不会在本机函数完成之后立即被调用 - 请记住通信是异步的。
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)title location:(NSString *)location callback: (RCTResponseSenderBlock)callback)
{
NSInteger eventId = ...
callback(@[@(eventId)]);
RCTLogInfo(@"Pretending to create an event %@ at %@", title, location);
}
然后可以使用以下方法在 JavaScript 中访问此方法
const onSubmit = () => {
CalendarModule.createCalendarEvent(
'Party',
'04-12-2020',
eventId => {
console.log(`Created a new event with id ${eventId}`);
},
);
};
本机模块应该只调用其回调一次。但是,它可以存储回调并在稍后调用它。此模式通常用于包装需要委托的 iOS API - 请参阅 RCTAlertManager
以了解示例。如果回调从未被调用,则会发生一些内存泄漏。
使用回调进行错误处理有两种方法。第一种是遵循 Node 的约定,并将传递给回调数组的第一个参数视为错误对象。
RCT_EXPORT_METHOD(createCalendarEventCallback:(NSString *)title location:(NSString *)location callback: (RCTResponseSenderBlock)callback)
{
NSNumber *eventId = [NSNumber numberWithInt:123];
callback(@[[NSNull null], eventId]);
}
在 JavaScript 中,您可以检查第一个参数以查看是否传递了错误
const onPress = () => {
CalendarModule.createCalendarEventCallback(
'testName',
'testLocation',
(error, eventId) => {
if (error) {
console.error(`Error found! ${error}`);
}
console.log(`event id ${eventId} returned`);
},
);
};
另一种选择是使用两个单独的回调:onFailure 和 onSuccess。
RCT_EXPORT_METHOD(createCalendarEventCallback:(NSString *)title
location:(NSString *)location
errorCallback: (RCTResponseSenderBlock)errorCallback
successCallback: (RCTResponseSenderBlock)successCallback)
{
@try {
NSNumber *eventId = [NSNumber numberWithInt:123];
successCallback(@[eventId]);
}
@catch ( NSException *e ) {
errorCallback(@[e]);
}
}
然后在 JavaScript 中,您可以为错误和成功响应添加单独的回调
const onPress = () => {
CalendarModule.createCalendarEventCallback(
'testName',
'testLocation',
error => {
console.error(`Error found! ${error}`);
},
eventId => {
console.log(`event id ${eventId} returned`);
},
);
};
如果您想将类似错误的对象传递给 JavaScript,请使用来自 RCTUtils.h.
的 RCTMakeError
。目前,这只会将一个类似于 Error 的字典传递给 JavaScript,但 React Native 旨在将来自动生成真正的 JavaScript Error 对象。您还可以提供一个 RCTResponseErrorBlock
参数,该参数用于错误回调并接受一个 NSError \* object
。请注意,此参数类型将不支持 TurboModules。
Promises
本机模块也可以实现一个 Promise,这可以简化您的 JavaScript,尤其是在使用 ES2016 的 async/await
语法时。当本机模块方法的最后一个参数是 RCTPromiseResolveBlock
和 RCTPromiseRejectBlock
时,其对应的 JS 方法将返回一个 JS Promise 对象。
将上面的代码重构为使用 Promise 而不是回调,如下所示
RCT_EXPORT_METHOD(createCalendarEvent:(NSString *)title
location:(NSString *)location
resolver:(RCTPromiseResolveBlock)resolve
rejecter:(RCTPromiseRejectBlock)reject)
{
NSInteger eventId = createCalendarEvent();
if (eventId) {
resolve(@(eventId));
} else {
reject(@"event_failure", @"no event id returned", nil);
}
}
此方法的 JavaScript 对应项返回一个 Promise。这意味着您可以在异步函数中使用 await
关键字来调用它并等待其结果
const onSubmit = async () => {
try {
const eventId = await CalendarModule.createCalendarEvent(
'Party',
'my house',
);
console.log(`Created a new event with id ${eventId}`);
} catch (e) {
console.error(e);
}
};
将事件发送到 JavaScript
原生模块可以在不直接调用 JavaScript 的情况下向 JavaScript 发送事件。例如,您可能希望向 JavaScript 发送一个提醒,提醒用户来自原生 iOS 日历应用程序的日历事件即将发生。首选方法是子类化 RCTEventEmitter
,实现 supportedEvents
并调用 self sendEventWithName
更新您的头文件类以导入 RCTEventEmitter
并子类化 RCTEventEmitter
// CalendarModule.h
#import <React/RCTBridgeModule.h>
#import <React/RCTEventEmitter.h>
@interface CalendarModule : RCTEventEmitter <RCTBridgeModule>
@end
JavaScript 代码可以通过在您的模块周围创建一个新的 NativeEventEmitter
实例来订阅这些事件。
如果您在没有监听器的情况下发出事件,而无谓地消耗资源,您将收到警告。为了避免这种情况,并优化您的模块的工作负载(例如,通过取消订阅上游通知或暂停后台任务),您可以在 RCTEventEmitter
子类中覆盖 startObserving
和 stopObserving
。
@implementation CalendarManager
{
bool hasListeners;
}
// Will be called when this module's first listener is added.
-(void)startObserving {
hasListeners = YES;
// Set up any upstream listeners or background tasks as necessary
}
// Will be called when this module's last listener is removed, or on dealloc.
-(void)stopObserving {
hasListeners = NO;
// Remove upstream listeners, stop unnecessary background tasks
}
- (void)calendarEventReminderReceived:(NSNotification *)notification
{
NSString *eventName = notification.userInfo[@"name"];
if (hasListeners) {// Only send events if anyone is listening
[self sendEventWithName:@"EventReminder" body:@{@"name": eventName}];
}
}
线程
除非原生模块提供自己的方法队列,否则它不应该对它被调用的线程做出任何假设。目前,如果原生模块没有提供方法队列,React Native 将为它创建一个单独的 GCD 队列并在那里调用它的方法。请注意,这是一个实现细节,可能会发生变化。如果您想为原生模块显式提供方法队列,请在原生模块中覆盖 (dispatch_queue_t) methodQueue
方法。例如,如果它需要使用仅限主线程的 iOS API,它应该通过以下方式指定:
- (dispatch_queue_t)methodQueue
{
return dispatch_get_main_queue();
}
类似地,如果操作可能需要很长时间才能完成,原生模块可以指定自己的队列来运行操作。同样,目前 React Native 将为您的原生模块提供一个单独的方法队列,但这是一个您不应该依赖的实现细节。如果您没有提供自己的方法队列,将来,您的原生模块的长时间运行操作可能会最终阻塞在其他无关的原生模块上执行的异步调用。例如,这里的 RCTAsyncLocalStorage
模块创建了自己的队列,这样 React 队列就不会阻塞等待潜在的缓慢磁盘访问。
- (dispatch_queue_t)methodQueue
{
return dispatch_queue_create("com.facebook.React.AsyncLocalStorageQueue", DISPATCH_QUEUE_SERIAL);
}
指定的 methodQueue
将由您模块中的所有方法共享。如果您的方法中只有一个是长时间运行的(或者由于某种原因需要在与其他方法不同的队列上运行),您可以在方法内部使用 dispatch_async
在另一个队列上执行该特定方法的代码,而不会影响其他方法。
RCT_EXPORT_METHOD(doSomethingExpensive:(NSString *)param callback:(RCTResponseSenderBlock)callback)
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// Call long-running code on background thread
...
// You can invoke callback from any thread/queue
callback(@[...]);
});
}
在模块之间共享调度队列
methodQueue
方法将在模块初始化时调用一次,然后由 React Native 保留,因此您无需自己保留对队列的引用,除非您希望在模块中使用它。但是,如果您希望在多个模块之间共享同一个队列,那么您需要确保为每个模块保留并返回同一个队列实例。
依赖注入
React Native 会自动创建和初始化任何已注册的原生模块。但是,您可能希望创建和初始化自己的模块实例,例如,注入依赖项。
您可以通过创建一个实现 RCTBridgeDelegate
协议的类来实现,使用委托作为参数初始化一个 RCTBridge
,并使用初始化的桥初始化一个 RCTRootView
。
id<RCTBridgeDelegate> moduleInitialiser = [[classThatImplementsRCTBridgeDelegate alloc] init];
RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:moduleInitialiser launchOptions:nil];
RCTRootView *rootView = [[RCTRootView alloc]
initWithBridge:bridge
moduleName:kModuleName
initialProperties:nil];
导出 Swift
Swift 不支持宏,因此将原生模块及其方法暴露给 React Native 中的 JavaScript 需要更多设置。但是,它的工作原理基本相同。假设您有相同的 CalendarModule
,但它是一个 Swift 类
// CalendarManager.swift
@objc(CalendarManager)
class CalendarManager: NSObject {
@objc(addEvent:location:date:)
func addEvent(_ name: String, location: String, date: NSNumber) -> Void {
// Date is ready to use!
}
@objc
func constantsToExport() -> [String: Any]! {
return ["someKey": "someValue"]
}
}
使用
@objc
修饰符非常重要,以确保类和函数正确导出到 Objective-C 运行时。
然后创建一个私有实现文件,它将向 React Native 注册所需的信息
// CalendarManagerBridge.m
#import <React/RCTBridgeModule.h>
@interface RCT_EXTERN_MODULE(CalendarManager, NSObject)
RCT_EXTERN_METHOD(addEvent:(NSString *)name location:(NSString *)location date:(nonnull NSNumber *)date)
@end
对于那些刚接触 Swift 和 Objective-C 的人来说,每当您在 iOS 项目中混合使用这两种语言时,您还需要一个额外的桥接文件,称为桥接头文件,以将 Objective-C 文件暴露给 Swift。如果您通过 Xcode 的 文件>新建文件
菜单选项将 Swift 文件添加到您的应用程序,Xcode 会为您提供创建此头文件的选项。您需要在此头文件中导入 RCTBridgeModule.h
。
// CalendarManager-Bridging-Header.h
#import <React/RCTBridgeModule.h>
您还可以使用 RCT_EXTERN_REMAP_MODULE
和 _RCT_EXTERN_REMAP_METHOD
来更改您正在导出的模块或方法的 JavaScript 名称。有关更多信息,请参阅 RCTBridgeModule
。
在创建第三方模块时很重要:使用 Swift 的静态库仅在 Xcode 9 及更高版本中受支持。为了使 Xcode 项目在您在模块中包含的 iOS 静态库中使用 Swift 时能够构建,您的主应用程序项目必须包含 Swift 代码和一个桥接头文件。如果您的应用程序项目不包含任何 Swift 代码,则可以使用一个空的 .swift 文件和一个空的桥接头文件作为解决方法。
保留的方法名称
invalidate()
原生模块可以通过实现 `invalidate()` 方法来符合 iOS 上的 RCTInvalidating 协议。当原生桥接器失效时(例如:在开发模式下重新加载),此方法 可以被调用。请根据需要使用此机制来完成原生模块所需的清理工作。