滴滴跨端框架 Hummer 的启动流程

Hummer 是滴滴开源的跨端框架,目前是滴滴货运司机端重要的开发三方库

一、加载导出类、方法和属性(在 OC 侧准备好被 JS 调用的类、方法和属性)

  1. 加载所有「已经导出的类」

    什么叫「导出的类 Exported Class」?指的是那些在 Native 侧已经写好了,TypeScript 代码可以使用的类。导出的方法,导出的属性同理。导出的类在Mach-O 文件中

objc 复制代码
导出一个类
#define HM_EXPORT_CLASS(jsClass, objcClass) \
__attribute__((used, section("__DATA, hm_export_class"))) \
static const HMExportStruct __hm_export_class_##jsClass##__ = {#jsClass, #objcClass};

举个例子
HM_EXPORT_CLASS(Loading, HMActivityIndicatorView)

存储的时候,用到了__attribute__((section("name"))) ,这个编译属性,改变了数据的存储特性。也就是说通过魔法一般的代码,我们得以在编译链接的时候就把对应的结构体(也就是 HMExportStruct)写到可执行文件 Mach-O 中,在后续的初始化代码中能读出来。

ARM 文档在这里,有兴趣就点去看看吧。

used 参数是告诉编译器:不管我用不用都不需要 优化掉这个函数。这里有一篇文章讲了怎么使用这个编译属性

于是我们的疑惑得以解开了:

举个栗子🌰给观众朋友们整明白点:

我们在 .m 文件中写下 HM_EXPORT_CLASS(Loading, HMActivityIndicatorView), 就足以让编译器把对应的 hm_export_class_Loding 结构体写到 Mach-O 文件中,然后在startEngine 的时候就能获取到对应的导出类信息(一组字符串而已)。

读取的时候使用到dladdr 函数,获取到 DL_info,然后根据 info获取共享对象的基地址(我猜测的Mach-O 文件的基地址),然后通过getsectbynamefromheader_64函数,获取Mach-O 文件的段数据。

arduino 复制代码
const struct section_64 *section = getsectbynamefromheader_64((void *) mach_header, "__DATA", "hm_export_class");
// 注意看这里的 hm_export_class 字符串,跟存储类信息时候的字符串对应上了

这时,就成功获取到了一个 HMExportClass 对象了(这是个单例对象)

less 复制代码
@interface HMExportClass : NSObject

@property (nonatomic, nullable, copy) NSString *className;
@property (nonatomic, nullable, copy) NSString *jsClass;

那什么叫加载呢?就是把这些Native 类起一个objc 的类名,再起一个 js 类名,然后放入一个 dictionary 中,这样既可以通过 objc 类名获取到这个类,也能通过 js 类名获取到这个类。一类,两名。就像给我家的仆人起一个中文名玛丽,起一个英文名 Mary,那既可以通过「玛丽」呼唤她,也可以通过「Mary」呼唤她。

  1. 加载导出类对应的导出的方法和属性

    导出的方法没放 Mach-O 文件里,而是定义了一个类方法

objc 复制代码
#define HM_EXPORT_PROPERTY(jsProp, getter, setter) \
+ (HMExportProperty *)__hm_export_property_##jsProp##__ { \
    HMExportProperty *exportProperty = [[HMExportProperty alloc] init]; \
    exportProperty.jsFieldName = @#jsProp; \
    exportProperty.propertyGetterSelector = @selector(getter); \
    exportProperty.propertySetterSelector = @selector(setter); \
\
    return exportProperty; \
}


#define HM_EXPORT_CLASS_METHOD(jsMethod, sel) \
+ (HMExportMethod *)__hm_export_method_class_##jsMethod##__ { \
    HMExportMethod *exportMethod = [[HMExportMethod alloc] init]; \
    exportMethod.jsFieldName = @#jsMethod; \
    exportMethod.selector = @selector(sel); \
\
    return exportMethod; \
}

所以直接通过class_copyMethodList 函数就能获取到所有方法和属性了,然后存到这个HMExportClass 对象的classMethodPropertyList 和 instanceMethodPropertyList

二、初始化 JSContext,创建 JS 执行的上下文,注册 C 函数到 JS执行上下文中

为了执行 JS 代码,我们需要一个 JSContext。

在 Hummer 框架中,就是HMJSContext ,抛开别的事情不看,实际执行 JS 代码的是HMJSContext 的一个属性 HMJSCExecutor 的 contextRef 属性,它是 C 语言的类型,表示 JS 代码执行的上下文 。具体类型是 JSGlobalContextRef

它跟 OC 语言中的JSContext可以互相转化。

所以,这一步最重要的,其实就是创建这个 context,给它的全局对象注册 C 函数,好让 js 能够调用这些C 函数,从而实现通信。

objc 复制代码
// 创建 JS 执行上下文
_contextRef = JSGlobalContextCreateInGroup(virtualMachineRef, NULL);

// 「通过 JS 执行上下文」执行JS 代码
JSValueRef result = JSEvaluateScript(self.contextRef, scriptRef, NULL, sourceRef, 1, &exception);

这一步还需要注册一些 C 函数给 JS 侧调用,具体代码是:

objc 复制代码
以 hummerCall 为例

JSObjectRef inlineHummerCallFunction = JSObjectMakeFunctionWithCallback(_contextRef, **NULL**, &hummerCall);

JSObjectSetProperty(_contextRef, globalThis, hummerCallString, inlineHummerCallFunction, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete, &exception);

原理是通过 JSObjectMakeFunctionWithCallback 和 JSObjectSetProperty 函数,把 C 函数 hummerCall 注册给了 JSContext 中的 globalThis 对象。JS 侧就能调用这些函数了。

参考资料

相关推荐
Meta Luo4 分钟前
AI 时代,Web 自动化测试该怎么做?
前端·人工智能·playwright 替代工具·无代码自动化测试工具·低代码 ui 自动化测试·web 回归测试工具
去伪存真7 分钟前
AI Agent 性能评估指标与自动化测试落地方案
前端·agent
CoderYanger31 分钟前
Java EE 进阶:6.SpringBoot 配置文件(第二弹)
java·spring boot·spring·面试·职场和发展·java-ee·程序员创富
MaAidan32 分钟前
用 Agent 排查线上问题:SLS 日志分析 + DB/Redis 数据验证,一条链路搞清楚
前端·bootstrap·html
墨林陌1 小时前
AI 热点日报(2026-11-03):美团发布 1.6 万亿参数国产大模型,四大厂商共推 AI 训练退出标准
前端
Moment1 小时前
DeepSeek 发布新模型,直接把 Agent 联网搜索的门槛打下来了!
前端·后端
AI宇宙狂想家1 小时前
开源 AI 视频工具进化史:从难用到好用,OpenMontage 改了什么
开发语言·前端·人工智能·开源·ai创业·openmontage
Rain的Java大神之路1 小时前
「设计一个在线文档协同编辑系统」:从 WebSocket、OT/CRDT 到快照回放,一篇讲透这道面试顶流题
java·后端·面试
Sailing1 小时前
🚀🚀【封闭结束7】Agent 为什么总是“失忆”?从长期记忆到上下文工程的生产实践
前端·人工智能·agent
OpenTiny社区1 小时前
AI 生成的 Vue 代码,终于能直接用了!
前端·vue.js·github