Node js 原生模块,从 objective-c 块事件侦听器触发回调不起作用

Node js native module, triggering callback from objective-c block event listener doesn't work

我需要创建一个本机节点模块来监听 objective-c OSX 事件,并在每次发生时触发对 javascript 的回调:

nativeAddon.listen(() => {
    console.log('It works!')
})

回调在 setUpCallback 函数中立即调用时有效,但它不会从 objective-c 观察者块中触发。

这是我的 main.mm 文件的样子

using namespace v8;

Local<Function> event_callback;

void setUpCallback(const FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = args.GetIsolate();
    HandleScope scope(isolate);

    // Store the callback to be used in the runCallback function
    Local<Function> cb = Local<Function>::Cast(args[0]);
    event_callback = cb;

    // THIS WORKS
    runCallback();

    // Listen to a mac event and trigger the callback when it happens
    [[[NSWorkspace sharedWorkspace] notificationCenter] addObserverForName:NSWorkspaceActiveSpaceDidChangeNotification object:NULL queue:NULL usingBlock:^(NSNotification *note) {
        // THIS DOESN'T WORK
        runCallback();
    }];
}

void runCallback() {
    auto isolate = Isolate::GetCurrent();
    HandleScope scope(isolate);

    Local<Context> context = isolate->GetCurrentContext();

    Local<Value> argv[1] = { String::NewFromUtf8(isolate, "hello world", NewStringType::kNormal).ToLocalChecked() };

    auto fn = Local<Function>::New(isolate, event_callback);
    fn->Call(context, Null(isolate), 1, argv).ToLocalChecked();
}

void Initialize(Local<Object> exports) {
    NODE_SET_METHOD(exports, "listen", setUpCallback);
}

NODE_MODULE(addon, Initialize)

如有任何帮助,我们将不胜感激!

尝试将 event_callback 的类型从 Local<Function> 更改为 Persistent<Function>

Local 的生命周期与创建它的 HandleScope 的生命周期相关。一旦 HandleScope 超出范围(在您的情况下,在function setUpCallback),所有在它处于活动状态时创建的 Locals 都将失效。如果你需要一个在 HandleScope 消失后 仍然存在 的句柄,你需要使用 Persistent。此处有更多详细信息:https://v8.dev/docs/embed#advanced-guide.

我正在使用 node-addon-api 并尝试了 @jmrk 的解决方案,但它对我不起作用。将类型更改为“持久”是不够的。

所以我在 GitHub here 上问了这个问题。

因为Mac事件是在单独的线程中触发的,所以需要使用ThreadSafeFunction.

#import <AppKit/AppKit.h>
#import <Foundation/Foundation.h>
#include <napi.h>

Napi::ThreadSafeFunction tsfn;

// Trigger the JS callback when active space changes
void listenForActiveSpaceChange(const Napi::CallbackInfo &info) {
  Napi::Env env = info.Env();

  // Create a ThreadSafeFunction
  tsfn = Napi::ThreadSafeFunction::New(
      env,
      info[0].As<Napi::Function>(), // JavaScript function called asynchronously
      "Active Space",               // Name
      0,                            // Unlimited queue
      1                             // Only one thread will use this initially
  );

  // Create a native callback function to be invoked by the TSFN
  auto callback = [](Napi::Env env, Napi::Function jsCallback) {
    // Call the JS callback
    jsCallback.Call({});
  };

  // Subscribe to macOS spaces change event
  [[[NSWorkspace sharedWorkspace] notificationCenter]
      addObserverForName:NSWorkspaceActiveSpaceDidChangeNotification
                  object:NULL
                   queue:NULL
              usingBlock:^(NSNotification *note) {
                // Perform a blocking call
                napi_status status =
                    tsfn.BlockingCall(callback);
                if (status != napi_ok) {
                  NSLog(@"Something went wrong, BlockingCall failed");
                }
              }];
}

Napi::Object init(Napi::Env env, Napi::Object exports) {
  exports.Set(Napi::String::New(env, "listenForActiveSpaceChange"),
              Napi::Function::New(env, listenForActiveSpaceChange));

  return exports;
};

NODE_API_MODULE(mac_helper, init);