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

如何从C方法向Objective-C类实现监听(委托模式)?

这个问题的核心是用C的函数指针搭建C和Objective-C之间的桥接层,完美对应你参考的C++委托模式,而且完全不需要修改旧的C日志代码(只要能让旧日志触发我们的通知函数就行)。下面是一步步的实现思路和代码:

核心思路

把C++的委托模式映射到C和OC的场景:

  • C层面用函数指针+上下文指针模拟C++的虚函数(Observer接口)
  • 维护一个C级别的回调列表,对应C++里的std::vector<ILoggerObserver*>
  • OC类作为观察者,把自身指针作为上下文注册到C回调,通过静态C函数桥接调用OC方法
具体实现步骤

1. 定义C桥接层的接口和逻辑

先写C的头文件和实现,用来管理回调列表和分发日志事件:

CLoggerBridge.h

#ifndef CLoggerBridge_h
#define CLoggerBridge_h

// 与原有C++日志级别对齐的C枚举
typedef enum {
    LOG_LEVEL_DEBUG,
    LOG_LEVEL_INFO,
    LOG_LEVEL_WARN,
    LOG_LEVEL_ERROR
} CLogLevel;

// 定义回调函数类型:参数包含日志级别、消息、上下文(用于传递OC对象)
typedef void (*CLogCallback)(CLogLevel level, const char* message, void* context);

// 注册回调(对应C++的Attach)
void CLoggerRegisterCallback(CLogCallback callback, void* context);

// 注销回调(对应C++的Detach)
void CLoggerUnregisterCallback(CLogCallback callback, void* context);

// 日志事件分发函数(对应C++的LogNotifier),供旧C日志调用
void CLoggerNotify(CLogLevel level, const char* message);

#endif /* CLoggerBridge_h */

CLoggerBridge.c

#include "CLoggerBridge.h"
#include <stdlib.h>
#include <string.h>
#include <pthread.h>

// 存储回调和上下文的结构体
typedef struct {
    CLogCallback callback;
    void* context;
} CallbackEntry;

// 全局回调列表及线程安全锁
static CallbackEntry* s_callbacks = NULL;
static size_t s_callbackCount = 0;
static size_t s_callbackCapacity = 0;
static pthread_mutex_t s_callbackMutex = PTHREAD_MUTEX_INITIALIZER;

void CLoggerRegisterCallback(CLogCallback callback, void* context) {
    if (!callback) return;

    pthread_mutex_lock(&s_callbackMutex);

    // 避免重复注册
    for (size_t i = 0; i < s_callbackCount; i++) {
        if (s_callbacks[i].callback == callback && s_callbacks[i].context == context) {
            pthread_mutex_unlock(&s_callbackMutex);
            return;
        }
    }

    // 动态扩容回调数组
    if (s_callbackCount >= s_callbackCapacity) {
        s_callbackCapacity = s_callbackCapacity == 0 ? 4 : s_callbackCapacity * 2;
        s_callbacks = realloc(s_callbacks, sizeof(CallbackEntry) * s_callbackCapacity);
    }

    // 添加新回调
    s_callbacks[s_callbackCount].callback = callback;
    s_callbacks[s_callbackCount].context = context;
    s_callbackCount++;

    pthread_mutex_unlock(&s_callbackMutex);
}

void CLoggerUnregisterCallback(CLogCallback callback, void* context) {
    pthread_mutex_lock(&s_callbackMutex);

    for (size_t i = 0; i < s_callbackCount; i++) {
        if (s_callbacks[i].callback == callback && s_callbacks[i].context == context) {
            // 移除回调,后面元素前移
            for (size_t j = i; j < s_callbackCount - 1; j++) {
                s_callbacks[j] = s_callbacks[j + 1];
            }
            s_callbackCount--;
            break;
        }
    }

    pthread_mutex_unlock(&s_callbackMutex);
}

void CLoggerNotify(CLogLevel level, const char* message) {
    pthread_mutex_lock(&s_callbackMutex);

    // 遍历所有回调并触发
    for (size_t i = 0; i < s_callbackCount; i++) {
        if (s_callbacks[i].callback) {
            s_callbacks[i].callback(level, message, s_callbacks[i].context);
        }
    }

    pthread_mutex_unlock(&s_callbackMutex);
}

2. 实现Objective-C观察者类

OC类通过静态C函数作为桥接,把C的回调事件转发到OC方法:

OCLogger.h

#import <Foundation/Foundation.h>
#include "CLoggerBridge.h"

// 与C日志级别对齐的OC枚举
typedef NS_ENUM(NSInteger, OCLogLevel) {
    OCLogLevelDebug = LOG_LEVEL_DEBUG,
    OCLogLevelInfo = LOG_LEVEL_INFO,
    OCLogLevelWarn = LOG_LEVEL_WARN,
    OCLogLevelError = LOG_LEVEL_ERROR
};

@interface OCLogger : NSObject

// 核心日志方法,替换成你的新OC日志类逻辑
- (void)logWithLevel:(OCLogLevel)level message:(NSString*)message;

// 注册为C日志的观察者(对应C++的Attach(this))
- (void)registerAsCLogObserver;

// 注销观察者(对应C++的Detach(this))
- (void)unregisterAsCLogObserver;

@end

OCLogger.m

#import "OCLogger.h"

// 静态C桥接函数:C层会调用这个函数,再转发给OC对象
static void CLogCallbackBridge(CLogLevel level, const char* message, void* context) {
    // 把void*上下文转回OC对象(__bridge避免ARC管理问题)
    OCLogger* logger = (__bridge OCLogger*)context;
    NSString* logMsg = [NSString stringWithUTF8String:message];
    [logger logWithLevel:(OCLogLevel)level message:logMsg];
}

@implementation OCLogger

- (void)registerAsCLogObserver {
    // 注册回调,把自身作为上下文传递
    CLoggerRegisterCallback(CLogCallbackBridge, (__bridge void*)self);
}

- (void)unregisterAsCLogObserver {
    CLoggerUnregisterCallback(CLogCallbackBridge, (__bridge void*)self);
}

- (void)logWithLevel:(OCLogLevel)level message:(NSString*)message {
    // 这里替换成你的新Objective-C日志类的实际逻辑
    switch (level) {
        case OCLogLevelDebug:
            NSLog(@"[DEBUG] %@", message);
            break;
        case OCLogLevelInfo:
            NSLog(@"[INFO] %@", message);
            break;
        case OCLogLevelWarn:
            NSLog(@"[WARN] %@", message);
            break;
        case OCLogLevelError:
            NSLog(@"[ERROR] %@", message);
            break;
    }
}

- (void)dealloc {
    // 销毁时必须注销回调,避免野指针崩溃
    [self unregisterAsCLogObserver];
}

@end

3. 关联旧C日志与桥接层

如果旧的C日志文件不能修改,你需要让它的Log函数触发CLoggerNotify:

  • 如果旧日志是通过宏定义输出,比如#define LOG(level, msg) CLog(level, msg),可以修改宏为#define LOG(level, msg) do { CLog(level, msg); CLoggerNotify(level, msg); } while(0)
  • 如果旧日志是固定的函数(比如void CLog(CLogLevel level, const char* msg)),可以通过符号覆盖的方式:在你的项目中实现同名函数,先调用原日志函数(通过extern void OriginalCLog(CLogLevel level, const char* msg);声明),再调用CLoggerNotify,编译时确保你的函数优先被链接。
使用示例
// 创建OC日志观察者
OCLogger* logger = [[OCLogger alloc] init];
// 注册到C日志回调
[logger registerAsCLogObserver];

// 当旧C日志触发时,OCLogger的logWithLevel:message:会自动被调用
关键注意事项
  • 线程安全:C层的回调操作加了pthread锁,确保多线程下不会出现数组越界或竞态问题
  • 内存管理:OC对象销毁时必须注销回调,否则C层的上下文指针会变成野指针,导致崩溃(代码中已经在dealloc里处理)
  • 类型安全:日志级别枚举要和原有C++/C的定义完全对齐,避免转换错误

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:16:14