C++中調用OC代碼

前言

  最近項目中爲了方便維護,底層統一使用C++編寫。因爲是項目是作屏幕共享sdk,因此只能底層的壓縮、編解碼使用C++,屏幕捕獲部分Mac和win就本身實現了。那麼問題就來了,由於是面向接口編程,因此項目的入口都是c++來寫的,而屏幕捕獲是須要oc部分的代碼,就須要C++調用oc代碼了。c++

準備

以前只作過OC調動C++,因而Google了一下,在Stack Overflow上找到了這個回答。要看具體描述的能夠去連接看看,實現思路一共有兩種,我在這裏大概描述一下。第一種,因爲C++是不能直接調用OC的,因此須要經過C語言做爲中間層,即C++調用C,C調用OC,這樣就達到了C++調用OC的目的。第二種OC是能夠調用C++的,經過在外部聲名C++類,而後類具體實現放在OC類中,這樣C++類就可以調用OC類了,其餘須要調用OC的類,只須要調用外部聲名的類便可。git

實現

具體的實現方式有兩種,第一種是C語言方法接收oc對象指針和參數,而後把指針橋接爲具體的oc對象。第二種是用C++進行包裝,先聲名一個C++類,這裏稱爲A。而後在OC類中,這裏稱爲B,對A進行實現,由於這個實現實在OC語言裏的,因此在這裏是能夠直接調用OC代碼的。接下來聲名一個C++類C。類C經過持有類A來調用OC類B,即A(C++)->C(C++)->B(OC類)github

實現方式一 by C

MyObject-C-Interface.hobjective-c

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.cppcode

#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;
}

運行結果以下:
運行結果blog

存在的問題

在每次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;
}

運行結果
by c++ IMPL

總結

第一種經過C語言的方式來調用,使用起來更復雜,因此建議使用C++的方式來實現。須要注意的問題是C++不能管理OC對象的釋放,因此須要利用循環引用。

你能夠在這裏下載demo

參考:https://stackoverflow.com/questions/1061005/calling-objective-c-method-from-c-member-function

相關文章
相關標籤/搜索