You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过C++调用Objective-C Runtime实现macOS本地通知功能

macOS纯C++调用UNUserNotificationCenter发本地通知的修复方案

核心错误排查

你原有代码存在以下几个关键问题:

  • 没有提前申请通知权限:UNUserNotificationCenter要求必须先向用户请求通知授权,获得许可后才能正常推送通知
  • 类对象和实例对象混用:修改title、body属性、构造通知请求时,你错误传入了UNMutableNotificationContent的类对象contentClass,实际应该传入你创建的实例对象content
  • 直接访问实例变量不可靠:苹果系统类的内部实例变量名不保证稳定,应该调用官方公开的setter方法设置属性,不要通过object_setIvar直接改ivar
  • objc_msgSend未做类型强转:不同架构下可变参数的函数调用约定和固定参数不同,直接调用多参数的objc_msgSend会出现参数传递错误,必须先强转为对应参数类型的函数指针
  • 进程提前退出:添加通知请求是异步操作,你在调用后直接退出进程,通知还没来得及触发就被系统回收了,需要增加等待逻辑保证进程存活到通知触发

修复后的完整代码

#define OBJC_OLD_DISPATCH_PROTOTYPES 1
#include <CoreFoundation/CoreFoundation.h>
#include <objc/objc-runtime.h>
#include <unistd.h>

int main(int argc, char** argv) {
    // 1. 先申请通知权限
    id center = objc_msgSend((id)objc_getClass("UNUserNotificationCenter"), sel_registerName("currentNotificationCenter"));
    typedef void (*AuthorizationCompletionHandler)(bool granted, id error);
    void auth_handler(bool granted, id error) {
        if (!granted) return; // 权限被拒绝直接退出
    }
    // 强转objc_msgSend为固定参数的函数指针再调用
    ((void (*)(id, SEL, unsigned long long, AuthorizationCompletionHandler))objc_msgSend)(
        center,
        sel_registerName("requestAuthorizationWithOptions:completionHandler:"),
        1 << 0 | 1 << 1, // UNAuthorizationOptionAlert + UNAuthorizationOptionSound
        auth_handler
    );
    // 等待权限申请回调完成
    sleep(1);

    // 2. 构造通知内容
    Class contentClass = objc_getClass("UNMutableNotificationContent");
    id content = objc_msgSend((id)contentClass, sel_registerName("alloc"));
    content = objc_msgSend(content, sel_registerName("init"));
    // 调用setter方法设置属性,不要直接改ivar
    id title = objc_msgSend((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), "通知标题");
    ((void (*)(id, SEL, id))objc_msgSend)(content, sel_registerName("setTitle:"), title);
    id body = objc_msgSend((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), "这是纯C++调用Objective-C Runtime发送的本地通知");
    ((void (*)(id, SEL, id))objc_msgSend)(content, sel_registerName("setBody:"), body);

    // 3. 构造通知触发器
    Class triggerClass = objc_getClass("UNTimeIntervalNotificationTrigger");
    id trigger = ((id (*)(id, SEL, NSTimeInterval, bool))objc_msgSend)(
        triggerClass,
        sel_registerName("triggerWithTimeInterval:repeats:"),
        5,
        false
    );

    // 4. 构造通知请求
    id requestId = objc_msgSend((id)objc_getClass("NSString"), sel_registerName("stringWithUTF8String:"), "test_notification_id");
    Class requestClass = objc_getClass("UNNotificationRequest");
    id request = ((id (*)(id, SEL, id, id, id))objc_msgSend)(
        requestClass,
        sel_registerName("requestWithIdentifier:content:trigger:"),
        requestId,
        content,
        trigger
    );

    // 5. 添加通知请求到通知中心
    ((void (*)(id, SEL, id, void (^)(id error)))objc_msgSend)(
        center,
        sel_registerName("addNotificationRequest:withCompletionHandler:"),
        request,
        nil
    );

    // 等待通知触发
    sleep(6);
    // 释放资源
    objc_msgSend(content, sel_registerName("release"));
    return 0;
}

编译运行说明

  • 编译时需要链接依赖框架,编译参数示例:clang++ main.cpp -o notify_test -framework CoreFoundation -framework Foundation -framework UserNotifications -lobjc
  • 首次运行会弹出权限申请弹窗,点击允许后,后续运行就能正常收到通知
  • 如果收不到通知,可以到系统设置>通知>找到你编译的程序,确认通知权限已经开启
  • 如果你要集成到现有C++项目中,可以把通知相关逻辑封装为单独的macOS平台适配模块,不影响跨平台代码架构

内容的提问来源于stack exchange,提问作者hrn

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 22:54:10