Objective-C++的缺点?

Luk*_*uke 19 c++ coding-style objective-c objective-c++ ios

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

当然,混合两个对象模型有其固有的局限性以及混乱和混乱的可能性.我更好奇的是如何使用Objective-C++会影响编译过程,我可能会遇到的语法缺陷,可读性问题以及我如何避免这些,等等.我很想知道你对Objective-C++的体验有什么就像接近这个以及你可能有的提示一样.

jus*_*tin 47

ObjC++非常强大 - 您可以同时选择和混合问题所需的功能以及与C,ObjC和C++的接口.我已经使用它多年了.当然,有一些注意事项,并且了解它们是很好的,这样您就可以最大限度地减少可能遇到的问题:

汇编

当您开始创建非平凡的程序时,编译时间远远高于ObjC或C++.

在ObjC类型中声明C++类型有一些常用方法:

  • 不透明的类型
  • 前瞻性声明
  • 使用智能指针转发声明
  • 按价值

我只是掩盖这一点,因为从OP推断出你熟悉这两种语言.同样,这是关于ObjC++的介绍性主题的公开撰写之一.

鉴于C++类型:

class t_thing { public: int a; };
Run Code Online (Sandbox Code Playgroud)

您可以通过多种方式声明您的ivars:

不透明类型:

@interface MONClass : NSObject { void* thing; } @end
Run Code Online (Sandbox Code Playgroud)

这应该避免.擦除类型安全性并不好.两个前进选项将引入类型安全.

此变体与ObjC翻译兼容.

前瞻声明:

class t_thing;
@interface MONClass : NSObject { t_thing* thing; } @end
Run Code Online (Sandbox Code Playgroud)

这比opaque类型更好,但智能指针甚至更好 - 如果你习惯于编写现代C++,这很明显.

只要您的C++类型位于全局命名空间中,此变体就与ObjC转换兼容.

使用智能指针进行前向声明:

class t_thing;
@interface MONClass : NSObject { t_smart_pointer<t_thing> thing; } @end
Run Code Online (Sandbox Code Playgroud)

如果您打算设置转换防火墙(例如,使用PIMPL和转发以减少依赖关系),这是最好的.同样,ObjC对象已经经历了锁定和分配,因此分配C++类型并不是一个坏点.如果您有多个声明,则可能更愿意为实现创建包装类型以减少单个分配.

此变体与ObjC翻译兼容.

现在是提醒您使用ObjC++的编译器选项的好时机:GCC_OBJC_CALL_CXX_CDTORS.设置此标志后会发生什么?编译器生成隐藏的objc方法,这些方法调用C++ ivars的构造函数和析构函数.如果您使用GCC_OBJC_CALL_CXX_CDTORSC++,则ivars必须是默认的可构造的.如果不启用此标志,必须手动构建和破坏你的高德完美 -如果你构建两次或不重写子类的初始化,那么你所面对UB.

按价值:

#include "thing.hpp"    
@interface MONClass : NSObject { t_thing thing; } @end
Run Code Online (Sandbox Code Playgroud)

最高的依赖性.这是(一般来说)我选择的路线,我对此有些遗憾.我刚刚将事情转移到使用更多C++并使用智能指针组合(如上所述)来减少依赖性.

此变体与ObjC翻译兼容.

关于现代ObjC编译器的另一件事:编译器在二进制文件中列出了C++类型的ivars /结构.信不信由你,这可能会占用大量的二进制空间.

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

如果您的大部分程序仍在ObjC中并且您希望保持这种方式,那么您将需要求助于在全局命名空间中声明的类型的转发或通过对象工厂销售特化的不透明基类型.就个人而言,我只是使用了很多C++,这不是一个理想的选择,并且在全局类型中包装实现很快就变得很烦人.

同时,由于编译时间很长,反之亦然:如果你可以将实现的重要部分保留为C++,那么你将节省大量的编译时间.出于这个原因和ARC(下面)你可以通过尽可能保持原始Apple类型作为CF类型来获得很多,因此你可以继续构建没有ObjC扩展的C++程序.

句法

我很少有问题,我保持我的C++类非常严格:

  • 我默认禁止复制和分配.
  • 我很少为C++类型声明自定义运算符.

如果你对C++很棒,那么你可以避免这个问题,但我更喜欢编译器来捕捉我犯的愚蠢错误.

一个明显的问题是ObjC消息发送中的C++范围分辨率.这需要一个空间:

[obj setValue:::func(a)]; // << bad
[obj setValue: ::func(a)]; // << good
Run Code Online (Sandbox Code Playgroud)

可读性

我遇到的一个问题是我从未找到过支持ObjC++语法的代码格式化程序.

ObjC消息

  • ObjC Messaging并按值返回:在nil按值返回C++类型时,需要在消息传递之前进行检查.如果您传递的对象是nil,则结果将在现代运行时(x86_64和iOS)上归零内存.如果您使用该实例,则它是未定义的行为.

  • ObjC Messaging并通过引用返回:在nil通过引用返回C++类型时,需要在消息传递之前进行检查.如果您发送的对象是nil,那么结果将是未定义的行为(引用0/NULL).

为了克服ObjC Messaging问题,我通常使用这样的表单:

- (bool)selector:(std::string&)outValue;
Run Code Online (Sandbox Code Playgroud)

某些内部错误的返回值为false,成功时返回true.

然后你可以安全地写:

if (![obj selector:outString]) { /* bail here */ }
Run Code Online (Sandbox Code Playgroud)

杂记

  • ARC兼容性:ObjC++对ARC不利.主要原因是ARC没有遵循混合对象模型.示例:如果您尝试将ObjC成员放入C++类型,编译器将拒绝ARC下的程序.这不是一个真正的问题,因为使用ObjC++(假设你也使用SBRM),MRC很简单,但它可能是你程序生命周期的一个问题.

  • 合成属性:您必须为C++类型定义属性.

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

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