Disadvantages of Objective-C++?

Disadvantages of Objective-C++?

本文关键字:Objective-C++ of Disadvantages      更新时间:2023-10-16

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

当然,混合两个对象模型有其固有的局限性和潜在的混乱和混淆。我更好奇的是使用objective - c++将如何影响编译过程,我可能遇到的语法陷阱,可读性问题以及如何避免这些问题,等等。我很想听听你使用objective - c++的经历,以及你对实现这一目标有什么建议。

objc++是非常强大的——你可以为你的问题选择和混合你需要的特性,并同时与C、ObjC和c++交互。我已经用了很多年了。当然,有一些注意事项,最好了解它们,这样您就可以最大限度地减少可能遇到的问题:

当你开始创建重要的程序时,编译时间比ObjC或c++要高得多。

在ObjC类型中声明c++类型有几种常见的方法:

<<ul>
  • 不透明类型/gh>
  • 向前声明
  • 带智能指针的正向声明
  • 通过值
  • 我将对这一点略去,因为从OP中可以推断出您熟悉这两种语言。同时,这也是比较公开的关于objc++入门主题的文章之一。

    给定c++类型:

    class t_thing { public: int a; };
    

    你有很多方法来声明你的变量:

    不透明类型:

    @interface MONClass : NSObject { void* thing; } @end
    

    应该避免。消除类型安全是不好的。这两个forward选项将引入类型安全。

    此变体与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。设置此标志后会发生什么?编译器生成隐藏对象方法,这些方法调用c++变量的构造函数和析构函数。如果你使用GCC_OBJC_CALL_CXX_CDTORS,你的c++变量必须是默认可构造的。如果你不启用这个标志,你必须手动构造和销毁你的ivars-如果你构造它两次或不重写子类的初始化式,那么你将面临UB。

    由价值:

    #include "thing.hpp"    
    @interface MONClass : NSObject { t_thing thing; } @end
    

    最高的依赖。这是我选择的路线,对此我有些遗憾。我只是把事情转移到使用更多的c++,并使用智能指针的组合(如上所述)来减少依赖。

    此变体与ObjC翻译不兼容。

    关于现代ObjC编译器的另一件事:编译器在二进制文件中列出c++类型的ivars/结构。信不信由你,这会消耗大量二进制空间。

    这里的要点是程序可以采取多种形式。您可以混合使用这些技术来减少依赖,这是引入依赖防火墙的最佳地点之一,因为ObjC是非常动态的(它的方法必须在一次转换中导出),对象创建需要分配,锁定,引入引用计数系统-单个对象的初始化时间已经相对较高,并且实现将始终是隐藏的。

    如果你的大部分程序仍然是在ObjC中,你想保持这种方式,那么你将需要诉诸于在全局命名空间中声明的类型的转发,或者通过对象工厂提供专门化的不透明基类型。就我个人而言,我只是使用了太多的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下的程序。这不是一个真正的问题,因为MRC在objc++中非常简单(假设你也使用SBRM),但它可能是你的程序生命周期的一个问题。

    • 合成属性:你必须为c++类型定义你的属性。

    • 外部工具:除了Xcode的工具集,很少有程序可以很好地处理或识别objc++。文本编辑器,ide,实用程序。

    • 苹果的工具:在Xcode的实用程序中,Xcode对objc++的支持有点低。重构(不可用),导航(使用clang解析器改进),概述(相当原始),objc++可以破坏IB的实用程序,通常不支持项目升级。