C++中调用OC代码
前言
最近项目中为了方便维护,底层统一使用C++编写。由于是项目是做屏幕共享sdk,所以只能底层的压缩、编解码使用C++,屏幕捕获部分Mac和win就自己实现了。那么问题就来了,因为是面向接口编程,所以项目的入口都是c++来写的,而屏幕捕获是需要oc部分的代码,就需要C++调用oc代码了。
准备
之前只做过OC调动C++,于是Google了一下,在Stack Overflow上找到了这个回答。要看具体描述的可以去链接看看,实现思路一共有两种,我在这里大概描述一下。第一种,由于C++是不能直接调用OC的,所以需要通过C语言作为中间层,即C++调用C,C调用OC,这样就达到了C++调用OC的目的。第二种OC是可以调用C++的,通过在外部声名C++类,然后类具体实现放在OC类中,这样C++类就能够调用OC类了,其他需要调用OC的类,只需要调用外部声名的类即可。
实现
具体的实现方式有两种,第一种是C语言方法接收oc对象指针和参数,然后把指针桥接为具体的oc对象。第二种是用C++进行包装,先声名一个C++类,这里称为A。然后在OC类中,这里称为B,对A进行实现,因为这个实现实在OC语言里的,所以在这里是可以直接调用OC代码的。接下来声名一个C++类C。类C通过持有类A来调用OC类B,即A(C++)->C(C++)->B(OC类)
实现方式一 by C
MyObject-C-Interface.h
int MyObjectDoSomethingWith (void *myObjectInstance, void *parameter);
MyObject.h
@interface MyObject : NSObject
{
int someVar;
}
- (int)doSomethingWith:(void *)aParameter;
@end
MyObject.mm
@implementation MyObject
int MyObjectDoSomethingWith (void *self, void *aParameter)
{
// 通过将self指针桥接为oc 对象来调用oc方法
return [(__bridge id)self doSomethingWith:aParameter];
}
- (int) doSomethingWith:(void *) aParameter
{
//将void *指针强转为对应的类型
int* param = (int *)aParameter;
return *param / 2 ;
}
- (void)dealloc
{
NSLog(@"%s", __func__);
}
@end
MyCPPClass.h
class MyCPPClass {
public:
MyCPPClass();
~MyCPPClass();
int someMethod (void *objectiveCObject, void *aParameter);
void *self;
void setSelf(void *self);
};
MyCPPClass.cpp
#include "MyObject-C-Interface.h"
MyCPPClass::MyCPPClass()
{
}
MyCPPClass::~MyCPPClass()
{
}
int MyCPPClass::someMethod (void *objectiveCObject, void *aParameter)
{
// To invoke an Objective-C method from C++, use
// the C trampoline function
return MyObjectDoSomethingWith (objectiveCObject, aParameter);
}
void MyCPPClass::setSelf(void *aSelf)
{
self = aSelf;
}
main.mm
#include "MyCPPClass.hpp"
#import "MyObject.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
// insert code here...
NSLog(@"Hello, World!");
MyObject *object = [[MyObject alloc] init];
MyCPPClass *c = new MyCPPClass();
c->setSelf((__bridge void *)object);
int a = 12;
int result = c->someMethod((__bridge void *)object, &a);
NSLog(@"%d", result);
}
return 0;
}
运行结果如下:
存在的问题
在每次C++调用时都需要传递OC对象桥接为void *的指针,使用起来很不方便。
方式二 by C++ IMPL
MyObject-C-Interface.h
#ifndef MyObject_C_Interface_h__h
#define MyObject_C_Interface_h__h
class MyClassImpl
{
public:
MyClassImpl ( void );
~MyClassImpl( void );
void init( void );
int doSomethingWith( void * aParameter );
void logMyMessage( char * aCStr );
private:
void * self;
};
#endif /* MyObject_C_Interface_h__h */
需要注意的是,MyClassImpl的实现是放在OC中的
MyObject.h
NS_ASSUME_NONNULL_BEGIN
@interface MyObject : NSObject
{
int someVar;
}
- (int) doSomethingWith:(void *) aParameter;
- (void) logMyMessage:(char *) aCStr;
@end
NS_ASSUME_NONNULL_END
MyObject.mm
#include "MyObject-C-Interface.h"
typedef void(^RetainSelfBlock)(void);
@implementation MyObject
{
RetainSelfBlock _retainBlock;//通过这个block持有对象,造成循环引用,避免被释放
}
MyClassImpl::MyClassImpl( void )
: self( NULL )
{
}
MyClassImpl::~MyClassImpl( void )
{
[(__bridge id) self breakRetainCycly];
}
void MyClassImpl::init( void )
{
MyObject *object = [[MyObject alloc] init];
object->_retainBlock = ^{//循环引用
[object class];
};
self = (__bridge void *)object;
NSLog(@"%p", self);
}
int MyClassImpl::doSomethingWith( void *aParameter )
{
NSLog(@"%p", self);
return [(__bridge id)self doSomethingWith:aParameter];
}
void MyClassImpl::logMyMessage( char *aCStr )
{
[(__bridge id)self logMyMessage:aCStr];
}
- (int) doSomethingWith:(void *) aParameter
{
int result = 0;
// ... some code to calculate the result
return result;
}
- (void) logMyMessage:(char *) aCStr
{
NSLog( @"%s", aCStr );
}
//打破循环引用,释放对象
- (void) breakRetainCycly
{
_retainBlock = nil;
}
- (void)dealloc
{
NSLog(@"%s", __func__);
}
@end
在MyObject.mm中需要注意的是,由于OC是使用ARC来进行内存管理的,C++不能够管理OC对象的生命周期。在默认的情况下,临时变量会在autorelease pool每一次pop后被释放,所以在oc实现中要想对象不被释放,那就需要循环引用来帮忙了。
具体代码如下,在MyClassImpl初始化时,利用循环引用保证object不被释放,在MyClassImpl调用析构函数时,将block置空,打破循环引用,以此来释放oc对象
void MyClassImpl::init( void )
{
MyObject *object = [[MyObject alloc] init];
object->_retainBlock = ^{//循环引用
[object class];
};
self = (__bridge void *)object;
NSLog(@"%p", self);
}
MyClassImpl::~MyClassImpl( void )
{
[(__bridge id) self breakRetainCycly];
}
//打破循环引用,释放对象
- (void) breakRetainCycly
{
_retainBlock = nil;
}
MyCPPClass.hpp
#ifndef MyCPPClass_hpp
#define MyCPPClass_hpp
#include <stdio.h>
class MyClassImpl;
class MyCPPClass
{
enum { cANSWER_TO_LIFE_THE_UNIVERSE_AND_EVERYTHING = 42 };
public:
MyCPPClass ( void );
~MyCPPClass( void );
void init( void );
void doSomethingWithMyClass( void );
private:
MyClassImpl * _impl;
int _myValue;
};
#endif /* MyCPPClass_hpp */
MyCPPClass.cpp
#include "MyCPPClass.hpp"
#include "MyObject-C-Interface.h"
MyCPPClass::MyCPPClass( void )
: _impl ( NULL )
{ }
void MyCPPClass::init( void )
{
_impl = new MyClassImpl();
_impl->init();
}
MyCPPClass::~MyCPPClass( void )
{
if ( _impl ) { delete _impl; _impl = NULL; }
}
void MyCPPClass::doSomethingWithMyClass( void )
{
int result = _impl->doSomethingWith(&_myValue);
if ( result == cANSWER_TO_LIFE_THE_UNIVERSE_AND_EVERYTHING )
{
_impl->logMyMessage("Hello, Arthur!");
}
else
{
_impl->logMyMessage("Don't worry.");
}
}
main.mm
#include "MyCPPClass.hpp"
int main(int argc, const char * argv[]) {
@autoreleasepool {
MyCPPClass *temp = new MyCPPClass();
temp->init();
temp->doSomethingWithMyClass();
delete temp;
}
return 0;
}
运行结果
总结
第一种通过C语言的方式来调用,使用起来更复杂,所以建议使用C++的方式来实现。需要注意的问题是C++不能管理OC对象的释放,所以需要利用循环引用。
你可以在这里下载demo
参考:https://stackoverflow.com/questions/1061005/calling-objective-c-method-from-c-member-function
C++中调用OC代码的更多相关文章
- 在 Swift 中调用 OC 代码
前言 在 Swift 语言中,我们可以使用 Objective-C.C 语言编写代码,我们可以导入任意用 Objective-C 写的 Cocoa 平台框架.Objective-C 框架或 C 类库. ...
- ios中javascript直接调用oc代码而非通过改变url回调方式(转)
之前一个ios项目中,需要通过UIWebview来打开一个静态页面,并在静态页面中 调用相关object-c代码. 一.以前使用js调用object-c的方法 关于如何使用javascript调用ob ...
- JS与OC交互,JS中调用OC方法(获取JSContext的方式)
最近用到JS和OC原生方法调用的问题,查了许多资料都语焉不详,自己记录一下吧,如果有误欢迎联系我指出. JS中调用OC方法有三种方式: 1.通过获取JSContext的方式直接调用OC方法 2.通过继 ...
- 如何在Java中调用Python代码
有时候,我们会碰到这样的问题:与A同学合作写代码,A同学只会写Python,而不会Java, 而你只会写Java并不擅长Python,并且发现难以用Java来重写对方的代码,这时,就不得不想方设法“调 ...
- C#中调用PowerShell代码
在C#中调用PowerShell代码,很多时候Add是不好使的!要用AddScript!记录一下! using (Runspace runspace = RunspaceFactory.CreateR ...
- iOS-JS调用OC代码
监听时间点击 改变当前浏览器窗口地址 在js里调用OC代码,需要在网页上写一个协议,不是http协议 然后在OC的webView shouldStartloadWithRequest
- 在Java中调用Python代码
极少数时候,我们会碰到类似这样的问题:与A同学合作写代码, A同学只会写Python,不熟悉Java ,而你只会写Java不擅长Python,并且发现难以用Java来重写对方的代码,这时,就不得不想方 ...
- 在 OC 中调用 Swift 代码
1.在 Objective-C 项目中使用 Swift 代码 1)在 OC 项目中创建 .Swift 文件,文件中的格式为其本有的格式. 2)Xcode 提示是否创建 Objective-C brid ...
- ios--网页js调用oc代码+传递参数+避免中文参数乱码的解决方案(实例)
此解决方案原理: 1.在ViewController.h中声明方法和成员变量,以及webView的委托: // // ViewController.h // JS_IOS_01 // // Cr ...
随机推荐
- java.lang.ArrayIndexOutOfBoundsException 异常分析及解决
参考:http://blog.csdn.net/javaeeteacher/article/details/4485834 这是一个非常常见的异常,从名字上看是数组下标越界错误,解决方法就是查看为什么 ...
- python format函数的使用
转载自:http://www.cnblogs.com/kaituorensheng/p/5709970.html python自2.6后,新增了一种格式化字符串函数str.format(),威力十足, ...
- python学习笔记:python操作redis
Redis 是一个高性能的key-value数据库.它支持存储的value类型包括string(字符串).list(链表).set(集合).zset(sorted set --有序集合)和hash(哈 ...
- Python编写购物小程序
购物车要求: 用户名和密码存放于文件中 启动程序后,先登录,登录成功则让用户输入工资,然后打印商品列表,失败则重新登录,超过三次则退出程序 允许用户根据商品编号购买商品 用户选择商品后,检测余额是否够 ...
- 单机zookeeper部署伪集群
1.zookeeper介绍 ZooKeeper 是一个为分布式应用所设计的分布的.开源的协调服务.分布式的应用可以建立在同步.配置管理.分组和命名等服务的更高级别的实现的基础之上. ZooKeeper ...
- 7.Jmeter 快速入门教程--录制复杂web测试脚本
Jmeter的功能简单,不需要有脚本语言的编写经验,纯图形界面添加测试场景, 用起来上手很快.但是如果手动添加每一个web(http/https)请求,费时又费力.而且有可能最后手动编写的和实际发的请 ...
- 巧妙的使用jmeter来发送json格式数据
1. header-manager 修改content-type值. 如果不修改该值, 则默认会是urlencode的数据格式(例如a=5&b=6). 修改为json后,会告诉服务器,发送的数 ...
- 07、python的基础-->数据类型、集合、深浅copy
一.数据类型 1.列表 lis = [11, 22, 33, 44, 55] for i in range(len(lis)): print(i) # i = 0 i = 1 i = 2 del li ...
- 一步一步学Vue(六)https://www.cnblogs.com/Johnzhang/p/7242640.html
一步一步学Vue(六):https://www.cnblogs.com/Johnzhang/p/7237065.html 路由 一步一步学Vue(七):https://www.cnblogs.com ...
- SpringMVC入门及拦截器
SSM最后一个框架springmvc,其实上手特别简单.但是我昨天看一个深入源码的视频,差点GG.其实以前学过很多东西,都忘了,不敢说学会,现在有了本书,看过一遍之后.多多少少记住一些,权当我会用了, ...