手把手教你开发 JSDebugger 插件:从 JD_REGISTER_PLUGIN 宏到全局 JS 函数
【免费下载链接】JSDebuggerJavaScript-Based Debugger For Inspecting Running State Of Your Application项目地址: https://gitcode.com/gh_mirrors/js/JSDebugger
JSDebugger 是一款基于 JavaScript 的 iOS 应用运行时调试工具,它最大的亮点是:不需要手动定义任何 JSExport,就能在 JavaScript 里直接调用任意 Objective-C 方法,随时查看并修改 App 的运行状态。而让 JSDebugger 如此灵活的秘密武器,正是它的插件机制。本文将以新手友好的方式,带你完整走一遍 JSDebugger 插件开发流程——从认识 JD_REGISTER_PLUGIN 宏,到让自定义函数出现在 JS 全局环境中。
什么是 JSDebugger 插件机制?🤔
JSDebugger 把"扩展调试能力"这件事完全交给了插件。官方内置了两个非常实用的插件:
- choose():遍历堆内存,找出指定类在运行时的所有实例
- introspect():检查某个对象的所有属性信息
这两个函数在 JS 里用起来就像普通全局函数一样自然:
var vc = choose(ViewController)[0]; // 找到所有 ViewController 实例 var info = introspect(vc); // 查看它的全部属性它们不是写死在引擎里的,而是通过一套统一的插件机制注册进去的。你完全可以照着它们的样子,写出属于自己的插件。
插件机制的三个关键角色 🧩
JSDebugger 插件机制由三部分构成:
| 角色 | 职责 | 源码位置 |
|---|---|---|
| JDFunctionPlugin 协议 | 规定插件必须实现的方法 | Source/Core/Plugin/JDFunctionPlugin.h |
| JDFunctionPluginManager | 收集并注册所有插件 | Source/Core/Plugin/JDFunctionPlugin.m |
| JD_REGISTER_PLUGIN 宏 | 一行代码完成"声明 + 注册" | 同上 |
JDFunctionPlugin 协议:插件的"身份证" 📇
协议只要求实现两个方法:
- key:这个插件在 JS 中叫什么名字(比如
"choose") - function:这个插件对应的 C 回调函数指针
JDFunctionPluginManager:插件的"大管家" 🧑💼
它是一个单例,内部维护一个插件数组。addPlugin:负责收集插件,registerPluginsIntoContext:负责把收集到的插件全部写入 JS 全局对象,本质就是用 JavaScriptCore 的JSObjectMakeFunctionWithCallback为每个插件创建一个全局函数。引擎启动时(Source/Core/JDEngine.m的start方法)会自动调用它,所以你完全不需要手动干预。
JD_REGISTER_PLUGIN 宏:三行代码解决注册 ⚡
宏的完整定义是理解插件机制的核心,建议收藏:
#define JD_REGISTER_PLUGIN(IMP, NAME) \ FOUNDATION_EXTERN void JDRegisterPlugin(Class pluginClass); \ + (void)load {JDRegisterPlugin(self);} \ - (NSString *)key {return @NAME;} \ - (JSObjectCallAsFunctionCallback)function {return IMP;}拆开看,它其实帮我们自动完成了三件事:
- 声明注册函数
JDRegisterPlugin - 在
+load时机自动注册:类一被加载,就立刻把自己交给插件管理器,无需手动调用 - 自动实现协议方法:
key返回宏里的 NAME,function返回宏里的 IMP 函数指针
也就是说,你只需要写一行JD_REGISTER_PLUGIN,插件就自动被收编进系统了,就是这么简单。🎯
认识官方内置插件:choose 与 introspect 🔍
choose的完整实现约 150 行(C++ 与 Objective-C 混合),核心是通过遍历 malloc 内存区域、比对 isa 指针来定位目标类的实例。但它的插件声明部分只有一行:
JD_REGISTER_PLUGIN(&choose, "choose");introspect更简洁,本质是把对象的所有属性转成数组返回:
JD_REGISTER_PLUGIN(&introspect, "introspect");参考源码:JDChoose.mm、JDIntrospect.mm。
手把手开发第一个 JSDebugger 插件 🛠️
现在轮到你了!我们来做一个简单但完整的示例:给 JS 增加一个version()函数,返回当前 App 的版本号。
第一步:创建插件类并遵守协议
新建一个 Objective-C 类,声明遵守JDFunctionPlugin协议,并引入头文件。
第二步:写 C 回调函数
实现一个JSObjectCallAsFunctionCallback类型的 C 函数。当 JS 里调用version()时,这个函数就会被触发,并返回一个 JSValueRef:
static JSValueRef version(JSContextRef ctx, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef *exception) { // 返回一个 JS 字符串 "1.0.0" return JSValueMakeString(ctx, JSStringCreateWithUTF8CString("1.0.0")); }第三步:用宏注册
在@implementation里写一行即可:
JD_REGISTER_PLUGIN(&version, "version");第四步:启动引擎并在 JS 中调用 🚀
App 启动时调用[[JDEngine engine] start];,之后在 JS 里就能直接使用:
var v = version(); console.log(v); // 输出 1.0.0四个实用小贴士 💡
- 注册时机是
+load:插件类只要被链接进工程就会自动注册,无需手动初始化任何东西。 - IMP 参数必须是函数指针:注意宏里传的是
&version取地址,不是方法实现。 - 注册后的函数不可删除:插件写入全局对象时带有
kJSPropertyAttributeDontDelete属性,在 JS 里delete是无效的。 - 先用官方 demo 验证:可以运行
JSDebuggerDemo/demo.js,里面已经演示了choose()等函数的完整用法,是很好的入门参考。
如何快速跑起来体验插件开发 🏃
最简单的方式是用 CocoaPods 集成(参考JSDebugger.podspec与JSDebuggerDemo/Podfile),也可以直接 clone 官方 Demo 工程:
git clone https://gitcode.com/gh_mirrors/js/JSDebugger打开 JSDebuggerDemo 工程后,配合Playground 热加载功能(JDLocalFileObserver监听文件变化,保存即自动重新执行 JS),改一行脚本马上看到效果,插件开发调试的体验会非常丝滑。✨
结语 🎉
从JD_REGISTER_PLUGIN宏到全局 JS 函数,JSDebugger 的插件机制把"给调试器添加新能力"这件事压缩到了极致:一个 C 函数 + 一行宏。掌握了它,你就能按需定制自己的调试工具箱——无论是批量查找对象、动态修改 UI,还是注入自定义调试命令,都能信手拈来。快去动手试试吧!
【免费下载链接】JSDebuggerJavaScript-Based Debugger For Inspecting Running State Of Your Application项目地址: https://gitcode.com/gh_mirrors/js/JSDebugger
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考