最近,开始了一个小项目。项目中需要用到一个 C++
库用于长连接通信(此库属于自研,而开发者没有Objective-C
开发经验)。由于库只提供了接口文件.h
和 .a
,需要使用 .h
文件提供的接口协议将数据传递到 Objective-C
这边用于展示。这个过程就涉及到了Objective-C
与 C++
库数据的传递对接。
整体思路:
在 Objective-C
注册回调到 C++
的实现文件中去,然后待信道收到数据后通过注册的回调将数据传出来。传出来之后将数据经过Objective-C
的对象包装之后再传递出去供使用。
稍微有点抽象,下面我们按照上面的思路一步步看实现。由于仅仅为了演示数据传递的过程,以下均为脱敏过后的精简代码。
首先来看一下C++
库提供的.h
都有什么。
// 回调接口
class ImCallback
{
public:
ImCallback();
virtual ~ImCallback();
// 接收消息(一对一、一对多)
virtual void OnMessageReceived(const ImMessage *message);
};
// 主动调用接口
class IMMANAGER_API ImManager
{
public:
ImManager();
virtual ~ImManager();
// 初始化SDK
uint32_t Init(ImCallback *callback);
};
ImCallback
是协议类,里面有一个OnMessageReceived
的协议方法是用来接收数据的,需要我们去实现。下面的 ImManager
类则是信道接口类,提供了一个初始化方法uint32_t Init(ImCallback *callback)
。参数为遵循 ImCallback
协议的类对象。接下来我们建立一个继承自 ImCallback
的类ImCallbackImpl
并实现 OnMessageReceived
方法。如下
#ifndef ImCallbackCpp_hpp
#define ImCallbackCpp_hpp
class ImCallbackImpl : public ImCallback
{
// 实现接收消息(一对一、一对多)
void OnMessageReceived(const ImMessage *message)
{
// do something
}
};
#endif
我们实现了ImCallback
中的协议方法void OnMessageReceived(const ImMessage *message)
。但在函数内部目前我们什么都没有做。
接下来我们需要实现在函数内部将ImMessage
的对象message
传出去。如下是ImCallbackImpl
实现的完整版。
#ifndef ImCallbackCpp_hpp
#define ImCallbackCpp_hpp
#include <stdio.h>
#include "im_manager.h"
#include <iostream>
typedef void (*OnMessageReceivedHandler) (const ImMessage *message);
class ImCallbackImpl : public ImCallback
{
private:
OnMessageReceivedHandler messageReceivedHandler;
public:
virtual void registerOnMessageReceivedHandler(OnMessageReceivedHandler handler) {
this->messageReceivedHandler = handler;
}
// 实现接收消息(一对一、一对多)
void OnMessageReceived(const ImMessage *message)
{
this->messageReceivedHandler(message);
}
};
#endif
上面我们声明了一个回调函数 OnMessageReceivedHandler
和回调函数注册方法 registerOnMessageReceivedHandler
。还一个私有成员变量messageReceivedHandler
用于存储外界注册进来的回调,然后在OnMessageReceived
方法中将数据传出去。C++
这边的代码已经 ready,接下来看一下 Objective-C
方的对接代码。
新建IMService
类,并将其.m
后缀更改为.mm
。直接上代码
IMService.h
#import <Foundation/Foundation.h>
@class MsgModel;
NS_ASSUME_NONNULL_BEGIN
@protocol IMServiceDelegate <NSObject>
- (void)onMessageReceived:(HBMsgModel *)model;
@end
@interface IMService : NSObject
@end
NS_ASSUME_NONNULL_END
IMService.mm
#import "IMService.h"
#import "im_manager.h"
#include "ImCallbackCpp.hpp"
#import "HBMsgModel.h"
@interface IMService ()
@property (nonatomic, weak) id<IMServiceDelegate> delegate;
@end
@implementation IMService {
ImManager *_imManager;
ImCallbackImpl *_imCallback;
}
- (instancetype)init {
self = [super init];
if (self) {
_imManager = CreateImManager();
_imCallback = new ImCallbackImpl;
_imManager->Init(_imCallback);
// 注册回调
_imCallback->registerOnMessageReceivedHandler(hb_messageReceivedHandler);
}
return self;
}
- (void)startWith:(NSDictionary *)configuration delegate:(id<HBIMServiceDelegate>)delegate {
// 处理 configuration
...
// set delegate
self.delegate = delegate;
}
void gk_messageReceivedHandler(const ImMessage *message) {
if (self.delegate && [self.delegate respondsToSelector:@selector(onMessageReceived:)]) {
MsgModel *model = [HBMsgModel new];
model.from = [NSString stringWithCString:message->from encoding:NSUTF8StringEncoding];
model.to = [NSString stringWithCString:message->to encoding:NSUTF8StringEncoding];
model.session_type = message->session_type;
model.content_type = message->content_type;
model.content_length = message->content_length;
model.message_id = [NSString stringWithCString:message->message_id encoding:NSUTF8StringEncoding];
model.timestamp = message->timestamp;
model.content = [NSString stringWithCString:message->content encoding:NSUTF8StringEncoding];
[self.delegate onMessageReceived:model];
}
}
@end
在IMService.mm
中我们调用了ImCallbackImpl
中的注册函数,将函数gk_messageReceivedHandler
的地址传了进去。ImCallbackImpl
中的void OnMessageReceived(const ImMessage *message)
方法在收到消息后,就会通过回调将里面的 message
数据传出来。这里,我新建了一个MsgModel
类,将数据转成MsgModel
对象后,通过代理
传了出去。哪里需要数据,你只要初始化一个IMService
对象并实现其代理方法即可实时的收取到信道发送过来的数据。接到数据后想怎么展现就随UI
设计心情了。
至此,就完成了原始C++
库与Objective-C
之间的数据传递和对接。如有错误,欢迎指正交流。
遇到的坑
在IMService.mm
中的void gk_messageReceivedHandler(const ImMessage *message)
函数内部将数据传递出去的过程中,为了减少频繁的对象创建,我一开始在函数外部创建了一个静态MsgModel
对象。
static MsgModel *model = [HBMsgModel new];
然后在函数内部为 model
的成员变量赋值然后再传出去。
信道收取信息不频繁时,这样做挺好的。后来遇到信道接收消息特别频繁的情况,此时传出去的数据可能来不及处理就发生了改变,从而导致部分数据丢失的情况。更改之后,才成功填坑!
Enjoy!
网友评论