Objective-C++的缺点是什么?

19

我正在使用Objective-C++为iOS编写一个大型项目。 我主要使用Objective-C来处理UI和其他苹果API,而使用C ++来处理内部音频处理和其他信息处理。 我想知道自由混合Objective-C和C++的缺点。

当然,混合两个对象模型具有其固有的限制和潜在的凌乱和混淆的可能性。 我更加好奇使用Objective-C++将如何影响编译过程,我可能会遇到的语法陷阱,可读性问题以及如何避免这些问题等。 我很感兴趣听听您在使用Objective-C++方面的经验以及您接近此问题的提示。


1
@Justin 虽然我们理解你想阅读关于这个主题的文章,但是请考虑一下这样的问题:“我是一个 Python 程序员。写 C 代码时可能会遇到哪些陷阱?” 这对目标受众肯定很有趣 - 我同意。 但这不适合 StackOverflow 的格式,而且提问者的问题更广泛,需要更多的讨论。 在这里会有多少篇文章被写出来,有哪些观点,谁会得到他们的“答案”? - HostileFork says dont trust SE
2
@Justin 如果某个主题的信息稀缺使得它成为一个好问题(因此广泛的信息则是一个不好的问题),那么我建议你在jquery标签中提出这个问题。无论如何...你可以在博客上发表这个问题并通过评论链接到它,或者去http://meta.stackoverflow.com/并与决策者辩论,为什么他们决定像“我很想听听你们使用Objective-C++的经验”这样的短语是提示它可能是应该在SO之外进行的对话的线索...即使它们是有趣的对话(就像这个问题可能会是)。 - HostileFork says dont trust SE
3
这是怎么变成未关闭的?显然这个问题没有客观的(哈!)答案,它是一个讨论的邀请,并以此方式表达。我之所以没有投票关闭是因为我已经这样做了。 - Nicol Bolas
1
HostileFork:如果某个主题的信息稀缺使得一个问题变得好,那并不意味着对另一个主题的广泛信息可获得性就会使得一个问题变得差。这是一个典型的过早推断的例子...我并不是说我同意Justin的观点,我只是在说你的推理是错误的。 - Lajos Arpad
5
我理解人们的批评,但我认为贾斯汀的回答表明这个问题可以带来收益。我正在寻找像那样具体的信息,而不是空洞、带有个人观点的“我对Objective-C++的看法是……”的回答,尽管我能看出这个问题可能会引发后者。 - Luke
显示剩余7条评论
1个回答

48

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。

按值:

#include "thing.hpp"    
@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消息传递和按值返回:在按值返回C++类型时,您需要在向nil发送消息之前进行检查。如果您发送消息的对象是nil,那么结果将是现代运行时(x86_64和iOS)上的零内存。如果您使用该实例,则是未定义的行为。

  • ObjC消息传递和按引用返回:在按引用返回C++类型时,您需要在向nil发送消息之前进行检查。如果您发送消息的对象是nil,则结果将是未定义的行为(引用0/NULL)。

为了克服ObjC消息传递问题,我通常使用以下形式:

- (bool)selector:(std::string&)outValue;

如果某些内部错误发生时,返回值为false,成功时则为true。

然后您可以安全地编写:

if (![obj selector:outString]) { /* bail here */ }

杂项

  • ARC兼容性:ObjC++不适合使用ARC。主要原因是ARC无法处理混合对象模型。例如:如果您尝试将ObjC成员放入C++类型中,在ARC下,编译器将拒绝该程序。这并不是真正的问题,因为使用ObjC++进行MRC非常简单(假设您还使用了SBRM),但这可能会影响您程序的寿命。

  • 合成属性:您需要为C++类型定义自己的属性。

  • 外部工具:除了Xcode的工具集之外,几乎没有其他程序能够很好地处理或识别ObjC++。文本编辑器、IDE、实用工具等。

  • 苹果公司的工具:在Xcode的实用工具中,Xcode对ObjC++的支持有些低。重构(不可用)、导航(使用clang解析器改进)、大纲(相当原始),ObjC++可能会破坏IB的实用工具,项目升级通常不受支持。


1
好东西 - 这就是我在寻找的具体信息类型。 - Luke
@Luke 嘿!我还在努力工作呢 =) - justin
哈哈,现在更好的东西了。感谢详细的回答! - Luke
@Luke YW - 我想我现在完成了 =) - justin
仅供将来的谷歌搜索用户参考,这里的SBRM是普通人所知道的RAII,而不是Santa Barbara Rescue Mission - dashesy
这是一个很棒的常见问题解答--我想知道自2011年以来有没有任何变化。 - SMGreenfield

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接