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

发布于:2016-10-27 20:23,阅读数:2476,点赞数: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`: ```c++ #ifndef EMObjectCpp_h #define EMObjectCpp_h class EMObjectCpp { public: EMObjectCpp(); ~EMObjectCpp(); }; #endif /* EMObjectCpp_h */ ``` `EMObjectCpp.cpp`: ```c++ #include "EMObjectCpp.h" EMObjectCpp::EMObjectCpp() { printf("init\n"); } EMObjectCpp::~EMObjectCpp() { printf("deinit\n"); } ``` - 新建 Objective-C 类: `EMObject.h`: ```objectivec #import <Foundation/Foundation.h> #include "EMObjectCpp.hpp" @interface EMObject : NSObject - (nonnull instancetype)init; - (void)dealloc; @end ``` 在 oc 类中,我们需要加入刚刚创建的 C++ 类作为实体,就像这样: ```objectivec @interface EMObject : NSObject { @private EMObjectCpp * __obj; } @end ``` 在这里,如果你在头文件中直接`#include "EMObjectCpp.hpp"`,然后试图在代码中使用`EMObjectCpp`类的话,你会得到一个编译错误: ![](//cdn.blog.yuusann.com/img/posts/16006_1.jpg) 其原因是,在 Objective-C 的语法中`class`是非法的关键字。此处编译器将 C++ 源文件当成 Objective-C 源文件编译显然是有问题的。编译器在`预编译`阶段会把`include`的头文件贴到源文件里进行预编译,所以在这里我们必须要保证不出现 C++ 的代码。所以在此处我们只能用扩展(`Extension`)去加入 C++ 类。另外,需要将源文件扩展名改成`.mm`才能启动 Xcode 的 C++ 支持。所以 Objective-C 类的实现是这个样子的: ~~`EMObject.m`~~: `EMObject.mm`: ```objectivec #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`: ```c++ #ifndef EMObjectCpp_hpp #define EMObjectCpp_hpp #include <string> class EMObjectCpp { public: EMObjectCpp(); ~EMObjectCpp(); void sayHello(); std::string getString(); }; #endif /* EMObjectCpp_hpp */ ``` `EMObjectCpp.cpp`: ```c++ #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`: ```objectivec #import <Foundation/Foundation.h> @interface EMObject : NSObject - (nonnull instancetype)init; - (void)dealloc; - (void)sayHello; - (nonnull NSString *)getString; @end ``` `EMObject.mm`: ```objectivec #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 ``` 测试代码: ```objectivec #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; } ``` 运行结果: ![](//cdn.blog.yuusann.com/img/posts/16006_2.jpg) # Swift 混编 事实上 Swift 并不能直接使用 C++ 类,因此我们要做的是在上面的基础上将 Objective-C 代码桥接到 Swift 中。 桥接方法很简单,新建一个头文件: `bridge.h`: ```objectivec #ifndef bridge_h #define bridge_h #include "EMObject.h" #endif /* bridge_h */ ``` 在工程的此处加入桥接头文件的相对路径和文件名: ![](//cdn.blog.yuusann.com/img/posts/16006_3.jpg) 然后在 Swift 代码中直接使用就可以了。 测试代码: ```swift import Foundation autoreleasepool { let obj = EMObject.init() obj.sayHello() print("\(obj.getString())") } ``` 运行结果同上。 # 结语 在 iOS 和 Mac 项目中使用 C++ 代码还是比较方便的。只要绕过了封装 Objective-C 类时写法上的小坑就可以了。至于 oc 桥接到 Swift 更是方便,苹果在这方面替我们铺好了路,方便我们迁移项目。苹果选择了 oc 是历史原因,当时 oc 还是十分先进的。一直没有机会换掉它,就沿用到了今天。现在终于有机会换掉它了,Swift 是为了替代 oc 的地位而诞生的,也相信它最终也会走向成功。


评论:0条


返回列表

返回主页