如何在Swift和Objective-C中使用C++代码

发布于: 2016-10-27 20:23
阅读: 1354
评论: 0
喜欢: 11

本文将介绍如何在 iOS 和 Mac 开发中使用 C++ 代码。

本文只讨论了源码混编过程,并不涉及只有头文件的二进制库的使用过程。

引言

在跨平台开发中,一类是使用 Web 相关技术,另一类是使用 C++ 开发后进行移植。在一些性能要求比较高又需要跨平台的场合下使用 C++ 开发是非常常见的。一方面是 C++ 有较高的执行效率和较好的跨平台兼容性,另一方面是 C++ 开发人员更加注重性能。相比 C 语言来说,C++提供了面向对象封装和更强大的STL库。许多成熟的框架如FFmpeg等都是由 C++ 实现的。Xcode是支持C++代码编译的,我们可以很轻松地在项目中混编 C++ 代码。

Objective-C 混编

Objective-C 混编 C++ 名为 Objective-C++。

在 oc 中混编 C++ 代码,我们用的方法是封装(wrap)。即在 C++ 类的外面封装一层 Objective-C 类。C++ 对象的生命周期是手动管理的,而在 iOS 或 Mac 开发中,ARC 是非常棒的功能。在 C++类外封装一层 Objective-C 类以后,就能巧妙利用 ARC 帮我们管理对象的生命周期了。

写法非常简单,但写法上有个坑,在例子中说明:

  • 新建 C++ 类:

EMObjectCpp.hpp

#ifndef EMObjectCpp_h
#define EMObjectCpp_h

class EMObjectCpp
{
public:
    EMObjectCpp();
    ~EMObjectCpp();
};

#endif /* EMObjectCpp_h */

EMObjectCpp.cpp

#include "EMObjectCpp.h"

EMObjectCpp::EMObjectCpp()
{
    printf("init\n");
}


EMObjectCpp::~EMObjectCpp()
{
    printf("deinit\n");
}
  • 新建 Objective-C 类: EMObject.h
#import <Foundation/Foundation.h>
#include "EMObjectCpp.hpp"

@interface EMObject : NSObject

- (nonnull instancetype)init;
- (void)dealloc;


@end

  在 oc 类中,我们需要加入刚刚创建的 C++ 类作为实体,就像这样:

@interface EMObject : NSObject {
@private
    EMObjectCpp * __obj;
}
@end

在这里,如果你在头文件中直接#include "EMObjectCpp.hpp",然后试图在代码中使用EMObjectCpp类的话,你会得到一个编译错误: 其原因是,在 Objective-C 的语法中class是非法的关键字。此处编译器将 C++ 源文件当成 Objective-C 源文件编译显然是有问题的。编译器在预编译阶段会把include的头文件贴到源文件里进行预编译,所以在这里我们必须要保证不出现 C++ 的代码。所以在此处我们只能用扩展(Extension)去加入 C++ 类。另外,需要将源文件扩展名改成.mm才能启动 Xcode 的 C++ 支持。所以 Objective-C 类的实现是这个样子的:

EMObject.m

EMObject.mm

#include "EMObjectCpp.hpp"
#include "EMObject.h"

@interface EMObject () {
@private
    EMObjectCpp * __obj;
}
@end

@implementation EMObject

- (instancetype)init
{
    self = [super init];
    if (self) {
        __obj = new EMObjectCpp();
    }
    return self;
}

- (void)dealloc
{
    delete __obj;
    __obj = NULL;
}
@end

现在已经可以编译通过了,对象也可以正确创建,也可以在 AutoreleasePool Pop 的时候正常地被 ARC 释放。

另外 Xcode 默认是支持 STL 的,因此在这里加入了两个简单的方法。完整源码如下:

EMObjectCpp.hpp

#ifndef EMObjectCpp_hpp
#define EMObjectCpp_hpp

#include <string>

class EMObjectCpp
{
public:
    EMObjectCpp();
    ~EMObjectCpp();
    void sayHello();
    std::string getString();
};

#endif /* EMObjectCpp_hpp */

EMObjectCpp.cpp

#include "EMObjectCpp.hpp"


EMObjectCpp::EMObjectCpp()
{
    printf("init\n");
}


EMObjectCpp::~EMObjectCpp()
{
    printf("deinit\n");
}

void EMObjectCpp::sayHello()
{
    printf("Hello world!\n");
}

std::string EMObjectCpp::getString()
{
    return "Hello STL";
}

EMObject.h

#import <Foundation/Foundation.h>

@interface EMObject : NSObject

- (nonnull instancetype)init;
- (void)dealloc;

- (void)sayHello;
- (nonnull NSString *)getString;

@end

EMObject.mm

#include "EMObjectCpp.hpp"
#include "EMObject.h"

@interface EMObject () {
@private
    EMObjectCpp * __obj;
}
@end

@implementation EMObject

- (instancetype)init
{
    self = [super init];
    if (self) {
        __obj = new EMObjectCpp();
    }
    return self;
}

- (void)dealloc
{
    delete __obj;
    __obj = NULL;
}

- (void)sayHello
{
    __obj->sayHello();
}

- (NSString *)getString
{
    return [NSString stringWithUTF8String:__obj->getString().c_str()];
}

@end

测试代码:

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


int main(int argc, const char * argv[]) {
    @autoreleasepool {
        EMObject * obj = [EMObject new];
        [obj sayHello];
        printf("%s\n", [obj getString].UTF8String);
    }
    return 0;
}

运行结果:

Swift 混编

事实上 Swift 并不能直接使用 C++ 类,因此我们要做的是在上面的基础上将 Objective-C 代码桥接到 Swift 中。 桥接方法很简单,新建一个头文件:

bridge.h

#ifndef bridge_h
#define bridge_h

#include "EMObject.h"

#endif /* bridge_h */

在工程的此处加入桥接头文件的相对路径和文件名: 然后在 Swift 代码中直接使用就可以了。 测试代码:

import Foundation

autoreleasepool {
    let obj = EMObject.init()
    obj.sayHello()
    print("\(obj.getString())")
}

运行结果同上。

结语

在 iOS 和 Mac 项目中使用 C++ 代码还是比较方便的。只要绕过了封装 Objective-C 类时写法上的小坑就可以了。至于 oc 桥接到 Swift 更是方便,苹果在这方面替我们铺好了路,方便我们迁移项目。苹果选择了 oc 是历史原因,当时 oc 还是十分先进的。一直没有机会换掉它,就沿用到了今天。现在终于有机会换掉它了,Swift 是为了替代 oc 的地位而诞生的,也相信它最终也会走向成功。


Thanks for reading.

All the best wishes for you! 💕