ObjC++非常强大-您可以选择并混合您需要解决问题的功能,并同时与C、ObjC和C ++进行接口。我已经使用它多年了。当然,也有一些注意事项,了解它们可以最小化可能遇到的问题:
编译
开始创建复杂程序时,编译时间比ObjC或C ++要高得多。
有几种常见的方法可以在ObjC类型中声明C ++类型:
- Opaque类型
- 前向声明
- 带智能指针的前向声明
- 按值传递
由于OP已经熟悉这两种语言,因此我将简单介绍一下这个话题。此外,这是关于ObjC ++的一个更公开的介绍性主题之一。
给定C ++类型:
class t_thing { public: int a; };
你有多种方式来声明你的实例变量:
不透明类型:
@interface MONClass : NSObject { void* thing; } @end
应该避免这样做。擦除类型安全并不好。两个前向选项将引入类型安全。
此变体与ObjC翻译兼容。
前向声明:
class t_thing;
@interface MONClass : NSObject { t_thing* thing; } @end
这种方式比不透明类型更好,但是智能指针更好 - 如果你习惯写现代C ++,那么这很明显。
只要您的C ++类型在全局命名空间中,此变体即与ObjC翻译兼容。
使用智能指针进行前向声明:
class t_thing;
@interface MONClass : NSObject { t_smart_pointer<t_thing> thing; } @end
如果您打算设置翻译防火墙(例如使用PIMPL和转发来减少依赖项),那么这是最好的选择。此外,ObjC对象已经经过锁定和分配,因此分配C ++类型并不是一个坏点。如果您有多个声明,可以创建一个包装器类型来减少单个分配。
此变体与ObjC翻译不兼容。
现在是提醒您的好时机,有一个编译器选项与ObjC ++应该启用:GCC_OBJC_CALL_CXX_CDTORS
。当设置了此标志时会发生什么?编译器会生成隐藏的objc方法来调用您的C ++ ivars构造函数和析构函数。如果使用GCC_OBJC_CALL_CXX_CDTORS
,则您的C ++ ivars必须具有默认构造功能。如果未启用此标志,则必须手动完美地构造和销毁ivars-如果将其构造两次或未覆盖子类的初始化程序,则面临UB。
按值:
@interface MONClass : NSObject { t_thing thing; } @end
最高依赖性。一般来说,这是我选择的路线,但我对此有些后悔。我刚刚将事情转移到使用更多的C++和智能指针组合(如上所述)以减少依赖关系。
这个变体与ObjC翻译不兼容。
现代ObjC编译器的另一个问题是:编译器在二进制文件中布置了你的C++类型的ivars/结构。信不信由你,这可能会消耗大量的二进制空间。
这里的要点是程序可以采取多种形式。您可以混合使用这些技术来减少依赖关系,这是引入依赖防火墙的最佳位置之一,因为ObjC非常动态(其方法必须在一个翻译中导出),对象创建需要分配、锁定、引入到引用计数系统中-单个对象的初始化时间已经相对较长,并且实现将始终被隐藏。
如果您的程序仍然大部分采用ObjC并且希望保持这种状态,那么您将需要使用在全局命名空间中声明的类型的forwards或不透明基础类型,通过对象工厂提供特殊化。就个人而言,我使用了很多C++,这不是一个理想的选择,将实现包装在全局类型中很快变得烦人。
同时,由于编译时间很长,反之亦然:如果您可以将重要部分的实现保持为C++,那么您将节省大量编译时间。出于这个原因和ARC(下面),您可以通过在可能的情况下将原始Apple类型保留为CF类型来继续构建没有ObjC扩展的C++程序。
语法
我很少遇到问题,但我对我的C++类非常严格:
我默认禁止复制和赋值。
我很少为C++类型声明定制运算符。
如果您非常擅长C++,那么您可以避免这个问题,但我更喜欢编译器捕获我所犯的愚蠢错误。
一个明显的问题是在ObjC消息发送中的C++作用域解析。这需要一个空格:
[obj setValue:::func(a)]; // << bad
[obj setValue: ::func(a)]; // << good
易读性
我遇到的一个问题是,我从未找到一个能够很好地支持ObjC++语法的代码格式化程序。
ObjC消息传递
为了克服ObjC消息传递问题,我通常使用以下形式:
- (bool)selector:(std::string&)outValue
如果某些内部错误发生时,返回值为false,成功时则为true。
然后您可以安全地编写:
if (![obj selector:outString]) { }
杂项
ARC兼容性:ObjC++不适合使用ARC。主要原因是ARC无法处理混合对象模型。例如:如果您尝试将ObjC成员放入C++类型中,在ARC下,编译器将拒绝该程序。这并不是真正的问题,因为使用ObjC++进行MRC非常简单(假设您还使用了SBRM),但这可能会影响您程序的寿命。
合成属性:您需要为C++类型定义自己的属性。
外部工具:除了Xcode的工具集之外,几乎没有其他程序能够很好地处理或识别ObjC++。文本编辑器、IDE、实用工具等。
苹果公司的工具:在Xcode的实用工具中,Xcode对ObjC++的支持有些低。重构(不可用)、导航(使用clang解析器改进)、大纲(相当原始),ObjC++可能会破坏IB的实用工具,项目升级通常不受支持。