c++ - 存储稍后在 Node C++ 插件中调用的 JS 回调
问题描述
我正在为 macOS 创建一个 Node C++ 插件,因此我将 Objective-C 与 C++ 和 Node Addon API 混合使用。
我想为 Node JS 提供一个函数,该函数接收一个回调,以便稍后在调用 Obj-C 观察者时调用。这就是我试图实现这一目标的方式:
#include <node.h>
#import <Foundation/Foundation.h>
#import <AVFoundation/AVFoundation.h>
@interface JSCallback:NSObject {
//Instance variables
NSString *testStr;
v8::Local<v8::Context> context;
v8::Local<v8::Function> fn;
v8::Isolate* isolate;
}
@property(retain, nonatomic, readwrite) NSString *testStr;
@property(nonatomic, readwrite) v8::Local<v8::Context> context;
@property(nonatomic, readwrite) v8::Local<v8::Function> fn;
@property(nonatomic, readwrite) v8::Isolate* isolate;
@end
@implementation JSCallback
@synthesize testStr;
@synthesize context;
@synthesize fn;
@synthesize isolate;
@end
namespace demo {
using v8::FunctionCallbackInfo;
using v8::Isolate;
using v8::Local;
using v8::Object;
using v8::String;
using v8::Function;
using v8::Context;
using v8::Value;
static OSStatus audioOutputDeviceChanged(
AudioObjectID inObjectID,
UInt32 inNumberAddresses,
const AudioObjectPropertyAddress* inAddresses,
void* __nullable inClientData
) {
JSCallback *jsCb = *((__unsafe_unretained JSCallback **)(&inClientData));
printf("%s", [jsCb.testStr UTF8String]);
jsCb.fn->Call(jsCb.context, Null(jsCb.isolate), 0, {}).ToLocalChecked();
return noErr;
}
void setOnAudioOutputDeviceChange(const FunctionCallbackInfo<Value>& args) {
AudioObjectPropertyAddress address = {
kAudioHardwarePropertyDefaultOutputDevice,
kAudioObjectPropertyScopeGlobal,
kAudioObjectPropertyElementMaster,
};
Isolate* isolate = args.GetIsolate();
Local<Context> context = isolate->GetCurrentContext();
Local<Function> cb = Local<Function>::Cast(args[0]);
// cb->Call(context, Null(isolate), 0, {}).ToLocalChecked();
JSCallback *jsCb = [[JSCallback alloc]init];
jsCb.testStr = @"a test string #002";
jsCb.context = context;
jsCb.fn = cb;
jsCb.isolate = isolate;
OSStatus status = AudioObjectAddPropertyListener(
kAudioObjectSystemObject,
&address,
&audioOutputDeviceChanged,
jsCb
);
if (status != noErr) {
NSException *e = [NSException
exceptionWithName:@"OSStatus Error"
reason:[NSString stringWithFormat:@"OSStatus Error (status: %d)", status]
userInfo:nil];
@throw e;
}
}
void Initialize(Local<Object> exports) {
NODE_SET_METHOD(exports, "setOnAudioOutputDeviceChange", setOnAudioOutputDeviceChange);
}
NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)
}
我创建了一个JSCallback
类来保存触发 JS 回调函数所需的变量。然后将其作为inClientData
for传递AudioObjectAddPropertyListener
。
然后当audioOutputDeviceChanged
被调用时,我尝试使用我存储的变量来触发 JS 回调。但是,当我这样做时,JS 脚本崩溃,并且只打印以下内容(无堆栈跟踪):
#
# Fatal error in v8::HandleScope::CreateHandle()
# Cannot create a handle without a HandleScope
#
我认为这可能会发生,因为当setOnAudioOutputDeviceChange
返回时,它会释放(或类似的东西)变量(上下文、cb 和隔离)。因此它们在函数之外无法使用。我怎样才能解决这个问题?
如果需要,这是我使用插件的 JS 代码:
const addon = require('./addon/build/Release/addon');
addon.setOnAudioOutputDeviceChange(() => {
console.info('setOnAudioOutputDeviceChange called');
});
setTimeout(() => {
// This keeps the JS script alive for some time
console.log('timedout');
}, 200000);
这是我的binding.gyp
文件,尽管我怀疑它是相关的:
{
"targets": [
{
"target_name": "addon",
"sources": [
"addon.mm",
],
"xcode_settings": {
"OTHER_CFLAGS": [
"-ObjC++",
],
},
"link_settings": {
"libraries": [
"-framework Foundation",
"-framework AVFoundation",
],
},
},
],
}
解决方案
您的诊断对我来说似乎是合理的:“当setOnAudioOutputDeviceChange
返回时,它会释放(或类似的)变量(上下文、cb 和隔离)。因此它们在函数之外无法使用”。
这些变量由jsCb
对象保存,但您将其AudioObjectAddPropertyListener()
作为context
which is a传递给它void*
,因此没有任何东西保留该对象。相反,尝试将它作为(__bridge_retained void*)jsCb
orCFBridgingRetain(jsCb)
传递,这将在添加回调之前增加引用计数。然后你可以在你的 audioOutputDeviceChanged 函数中检索它JSCallback *jsCb = (__bridge JSCallback *)inClientData;
,它既不会保留也不会释放它。最后,在移除音频属性监听器时,您应该通过__bridge_transfer
或CFRelease
/释放对象CFBridgingRelease
。
此处和此处的文档中描述了桥接转换,您可以通过搜索 Stack Overflow 和其他地方找到更多示例。
或者,不是手动保留/释放 Obj-C 对象,您可以通过创建一个static
您初始化一次的对象(例如在您的Initialize
函数) 或NODE_MODULE_INITIALIZER
为“上下文感知插件”创建它。然后你可以__bridge
在传递这个对象时使用,void* context
而不用担心保留和释放。
推荐阅读
- java - 如何创建像“Map.Entry”这样的数组
“? - r - 在 R 中运行多个泊松回归模型时的对比错误:
- python - tensorflow-如何使用可变图像的大小进行 conv2d_transpose?
- android - 更新 Android Studio 后,Android 构建命令失败
- c# - 如何在我的 Web 项目中调用我的统一函数?
- angular5 - 无法使用 httpclient 访问 Angular 5 中的本地 json 数据
- python - Django计算含税总价
- asp.net - 如何在没有任何 web 服务和 webapi 的情况下从网站访问和编辑 HTML
- ios - 在 UIImageView 中加载 gif 图像时减少内存消耗
- c# - ListView 在单击时显示模式的详细信息