前言
在 iOS 开发中,JavaScriptCore 框架提供了强大的 JS 引擎,可以让我们在应用中运行 JavaScript 代码。而 JSExport 是这个框架中最重要的机制之一,它可以让我们将 Objective-C 的对象暴露给 JavaScript 调用,以使 JavaScript 可以像使用普通 JS 对象一样访问 Objective-C 的方法和属性。
我们先来了解下什么是 JSExport。
JSExport 是什么?
简单来说,JSExport 是一个协议,只要你定义的 Objective-C 协议继承自它,并让一个类遵循这个协议,就可以将该类的方法和属性导出为 JavaScript 可访问的接口。
@protocol JSExport
了解完概念,来看下它在代码中是如何使用的。
基本使用步骤
使用 JSExport 通常需要以下几步:
- 创建一个继承自
JSExport的协议; - 将希望暴露的方法或属性声明在协议中;
- 创建一个遵循该协议的 Objective-C 类;
- 将该类的实例注入到
JSContext中; - 在 JavaScript 中调用暴露出来的方法。
比如我们想把下面的代码暴露给 JS 调用,官方文档的示例代码如下:
@protocol MyPointExports <JSExport>
@property double x;
@property double y;
- (NSString *)description;
- (instancetype)initWithX:(double)x y:(double)y;
+ (MyPoint *)makePointWithX:(double)x y:(double)y;
@end
@interface MyPoint : NSObject <MyPointExports>
- (void)myPrivateMethod; // This isn't in the MyPointExports protocol, so it isn't visible to JavaScript code.
@end
@implementation MyPoint
@end
我们来分类看一下如何将 OC 的代码暴露给 JS 调用:
暴露属性
示例代码如下:
// 在协议中声明
@protocol MyPointExports <JSExport>
@property double x;
@property double y;
@end
// 类中合成属性的 set get 方法
#import "MyPoint.h"
@implementation MyPoint
@synthesize x;
@synthesize y;
@end
// 在 JS 中调用暴露的属性
JSContext *context = [[JSContext alloc] init];
Calculator *calc = [[Calculator alloc] init];
context[@"calc"] = calc;
MyPoint *point = [MyPoint new];
point.x = 10.2;
point.y = 12.1;
context[@"point"] = point;
JSValue *xResult = [context evaluateScript:@"point.x"];
JSValue *yResult = [context evaluateScript:@"point.y"];
NSLog(@"X:%f, Y:%f", [xResult toDouble], [yResult toDouble]);
打印结果:X:10.200000, Y:12.100000。
暴露实例方法
示例代码如下:
// 在协议中声明
@protocol MyPointExports <JSExport>
- (NSString *)description;
- (instancetype)initWithX:(double)x y:(double)y;
@end
// 在类中实现
- (NSString *)description {
return @"MyPoint";
}
- (instancetype)initWithX:(double)x y:(double)y {
if (self = [super init]) {
self.x = x;
self.y = y;
}
return self;
}
// 在 JS 中调用暴露的方法
JSContext *context = [[JSContext alloc] init];
context[@"MyPoint"] = [MyPoint class];
JSValue *result = [context evaluateScript:@"new MyPoint(1, 2)"];
NSLog(@"X:%f, Y:%f", [result[@"x"] toDouble], [result[@"y"] toDouble]);
打印结果:X:1.000000, Y:2.000000。
暴露类方法
// 在协议中声明
@protocol MyPointExports <JSExport>
@property double x;
@property double y;
- (instancetype)initWithX:(double)x y:(double)y;
+ (MyPoint *)makePointWithX:(double)x y:(double)y;
@end
// 在类中实现
@implementation MyPoint
@synthesize x;
@synthesize y;
+ (MyPoint *)makePointWithX:(double)x y:(double)y {
return [[MyPoint alloc] initWithX:x y:y];
}
- (instancetype)initWithX:(double)x y:(double)y {
if (self = [super init]) {
self.x = x;
self.y = y;
}
return self;
}
@end
// 在 JS 中调用暴露的类方法
JSContext *context = [[JSContext alloc] init];
context[@"MyPoint"] = [MyPoint class];
JSValue *result = [context evaluateScript:@"MyPoint.makePointWithXY(3, 4)"];
NSLog(@"X:%f, Y:%f", [result[@"x"] toDouble], [result[@"y"] toDouble]);
打印结果:X:3.000000, Y:4.000000。
总结
JSExport 是 JavaScriptCore 框架中连接 Objective-C 与 JavaScript 的核心机制,正确的使用可以让你灵活地在原生与脚本之间切换逻辑,适用场景如下:
- 配置型逻辑引擎;
- 脚本化功能扩展;
- 小程序平台;
- 混合框架。
使用 JSExport 的关键步骤:
- 明确协议继承
JSExport; - 方法命名符合规则;
- 类遵循协议;
- 注入实例到
JSContext。
通过这套机制,你可以让 JavaScript 像本地对象一样调用 Objective-C 类中的方法,极大提升了扩展性和灵活性。