JSPatch扩展机制详解:1行代码动态加载扩展,3行代码支持自定义Struct类型
【免费下载链接】JSPatchJSPatch bridge Objective-C and Javascript using the Objective-C runtime. You can call any Objective-C class and method in JavaScript by just including a small engine. JSPatch is generally used to hotfix iOS App.项目地址: https://gitcode.com/gh_mirrors/js/JSPatch
🔥JSPatch 扩展机制是 JSPatch 动态化引擎最实用的进阶能力:JSPatch 通过 Objective-C 运行时桥接 JavaScript 与 iOS 原生代码,常用于 iOS App 热修复(hotfix)。而它的**扩展机制(JPExtension)**让你只需1 行代码动态加载扩展,就能解锁 Block 回调、C 函数调用、协议实现等高级能力;再配合defineStruct接口,3 行代码即可支持自定义 Struct 类型在 JS 与 OC 之间自由转换。本文将用最短路径带你吃透这套机制。
什么是 JSPatch 扩展机制?
JSPatch 的核心是一个极小的引擎,源码仅 3 个文件:
- JPEngine.h
- JPEngine.m
- JSPatch.js
引擎本身只负责"JS 调 OC 方法"这件事,而 Block 回调、C 函数调用、协议定义等高级功能,全部以**扩展(Extension)**的形式独立放在Extensions/目录中。
每个扩展都继承自JPExtension基类(定义见 JPEngine.h),它要求实现一个类方法:
+ (void)main:(JSContext *)context;这个方法的职责是:往 JS 引擎的JSContext里"注入"若干全局函数(如block()、defineCFunction())。注入后,你的 JS 脚本就能直接调用这些函数——这就是 JSPatch 扩展机制的完整原理。
1行代码动态加载扩展:addExtensions 完整指南
🚀 加载扩展只需要一行代码,调用JPEngine的类方法:
[JPEngine addExtensions:@[@"JPBlock", @"JPCFunction", @"JPProtocol"]];addExtensions:的实现在 JPEngine.m:它会遍历传入的类名数组,通过NSClassFromString找到对应的扩展类,并逐个调用其main:方法完成注入。
⚠️ 新手常见误区:
- 顺序要求:必须先把扩展的源码加入工程编译,再调用
addExtensions:; - 类名即字符串:数组元素是类名字符串,写错会静默失败,注意检查;
- 加载时机:建议放在
startEngine之后、执行任何 JS 脚本之前。
内置扩展清单:按需加载即可
| 扩展 | 源码位置 | 提供的 JS 能力 |
|---|---|---|
| JPBlock | Extensions/JPBlock/ | block()从 JS 向 OC 传递 Block 回调 |
| JPCFunction | Extensions/JPCFunction/JPCFunction.h | defineCFunction()调用系统 C 函数 |
| JPStructPointer | Extensions/JPCFunction/JPStructPointer.m | newStruct()/pvalStruct()操作 Struct 指针 |
| JPProtocol | Extensions/JPProtocol.h | defineProtocol()用 JS 定义并实现协议 |
| JPNumber | Extensions/JPNumber.h | 修复 iOS 7 下NSNumber的精度问题 |
| JPLocker | Extensions/JPLocker.h | 跨 JS/OC 互斥锁,解决多线程调用冲突 |
| JPDispatch | Extensions/JPDispatch.h | 补充 GCD 调度函数 |
| JPCleaner | Extensions/JPCleaner.h | cleaner帮助 JS 持有对象正确释放 |
所有扩展源码集中在 Extensions/ 目录,可按需挑选,保持包体积最小。
3行代码支持自定义Struct类型:defineStruct 使用教程
CGRect、CGPoint、CGSize、NSRange 这 4 个 Struct 是 JSPatch 内置支持的(在 JPEngine.m 的JPForwardInvocation中硬编码处理)。但业务里经常遇到自定义 Struct,怎么办?
✨ 调用JPEngine的defineStruct:接口(声明见 JPEngine.h),3 行代码搞定:
[JPEngine defineStruct:@{ @"name": @"MyPoint", @"types": @"ff", @"keys": @[@"x", @"y"] }];三个键的含义:
| 键 | 含义 | 示例 |
|---|---|---|
name | OC 中的 Struct 类型名 | @"MyPoint" |
types | 各成员的类型编码串(f=float,d=double,i=int……) | @"ff"表示两个 float |
keys | JS 侧对应的属性名 | @[@"x", @"y"] |
注册之后效果立竿见影:
- OC 方法返回该 Struct 时,JS 里自动得到一个普通对象:
{x: 10, y: 20}; - JS 向 OC 方法传入该 Struct 时,直接传 JS 对象即可,引擎自动还原为 C 内存布局。
注册逻辑见 JPEngine.m:定义被存入_registeredStruct字典,JPForwardInvocation转发调用时会按类型编码自动完成双向转换。
Struct 指针也能操作:newStruct 与 pvalStruct
如果 OC 方法接收的是Struct 指针(如CGPoint *),则需要额外一步:先newStruct分配内存,再传指针,用完用pvalStruct读取——实现在 JPStructPointer.m:
var p = newStruct("CGPoint", {x: 1, y: 2}); // 分配并填充 var v = pvalStruct("CGPoint", p); // 读回 {x:1, y:2}进阶:3行JS调用系统C函数
JSPatch 扩展机制不止于 Struct。加载JPCFunction扩展后,可在 JS 里声明并调用任意系统 C 函数(如 CoreFoundation 的CFStringCreateWithCString),核心源码见 JPCFunction.m。它借助 JPLibffi/(libffi)完成函数签名解析,因此自定义 Struct 也能作为 C 函数的参数与返回值传递。
新手避坑清单 📌
- 忘记加扩展源码:
addExtensions:只是"加载",前提是扩展的 .m 文件已加入编译,否则NSClassFromString返回空; - types 写错:
defineStruct的类型编码串必须与 C 内存布局严格一致,float是f、double是d,差一个字符都会导致数据错乱; - 多线程冲突:JS 与 OC 并发访问时,建议同时加载
JPLocker扩展; - 对象释放:JS 侧创建的 OC 对象若需提前释放,配合
JPCleaner使用。
小结
- 1 行代码
addExtensions:→ 动态加载 JSPatch 扩展,按需解锁 Block、C 函数、协议等能力; - 3 行代码
defineStruct:→ 让自定义 Struct 类型在 JS 与 OC 之间无缝互转; - 所有扩展源码位于 Extensions/,核心调度逻辑在 JPEngine.m,建议对照源码阅读本文,理解会更深。
掌握 JSPatch 扩展机制,你的 iOS 热修复与动态化玩法将再上一个台阶 🎉
【免费下载链接】JSPatchJSPatch bridge Objective-C and Javascript using the Objective-C runtime. You can call any Objective-C class and method in JavaScript by just including a small engine. JSPatch is generally used to hotfix iOS App.项目地址: https://gitcode.com/gh_mirrors/js/JSPatch
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考