如何从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
相关产品推荐
相关产品推荐

