为什么 NSRect、NSPoint 等是结构体,而不是 类?

Why are NSRect, NSPoint, etc. structs, not classes?

我最近需要创建自己的类似于 NSRect 的类型,它有一个锚点(本质上是一个带有另一个 NSPoint 的 NSRect)。

some research 之后,我发现我实际上可能更好地将其设为 class(如在 NSObject subclass 中),而不是使用 struct。为什么 Apple 会制作这些类型结构,而不是 classes?看起来它会有很多好处,比如不必将它们包装在 NSValues 中,不必使用 C 函数与它们交互等等

我相信他们是有原因的。仅仅是内存使用量稍微减少了吗?只是历史的吗?还是我错过了更大的东西?

一般在有方法对数据进行操作时使用class。如果您只有数据,那么只需使用一个结构。 似乎没有任何有用的方法可以与 point 或 rect 一起使用,因此不需要 class.

每种编程范式都有其优缺点,object-oriented programming (OOP) 也不例外。

拥有 classes(即以面向对象的风格编写)的所有主要好处是 encapsulation, polymorphism and inheritance

如果我们声明像 Rect 或 Point a 这样的简单单位 class,我们将不会使用该列表中的任何东西,但我们会带来 OOP 的所有缺点,包括隐式可变性,更复杂内存管理、指针引用、隐式参与者、损坏的封装(使用 setters/getters)等。我没有在这里列出 OOP 中确实存在的数百个 anti-patterns

我怀疑在 Cocoa 中选择 Rect 和 Point 结构是否符合 C 遗留的要求,首先是因为 Objective-C 从一开始就支持 classes,其次因为全新的 Swift 语言没有这种传统,但几乎所有标准类型和容器在其标准库中都被声明为结构,而不是 classes。

一般来说,如果您不需要封装、多态性和继承 - 您应该考虑将新实体声明为结构并尽可能避免 OOP。

Why then has Apple make these types structs, not classes?

出于性能原因,这些 实体 经常使用、经常处理并且很小。将它们封装在对象中的开销并不能证明它们是对象的优点。

我认为这与以下事实有关:每个结构都保存原始类型的数据(通常是浮点数和双精度数),它们自己没有任何操作(如函数或方法),因此不需要更复杂的东西就像功能齐全的 class(没有导入,也没有样板代码,例如初始化器),当然,正如其他同事提到的,因为 Objective-C 是 C 的超集。

如果您已经使用 Objective-C 一段时间,那么您就会知道它是 coreFoundation 之上的一个 OOP 层,它是所有结构和 'opaque types'(具有秘密的结构)..

例如,NSDictionary 实际上只是 CFtype CFDictionaryRef 的包装器。好吧,您在这里询问的内容、大小、点、矩形、edgeInsets 等只是确定不保证升级的小数据类型。基元(int、double、bool 等)也没有获得 (public) class 的免费桥梁。他们得到了 NSNumber 集群,这些人都聚集在 NSValue 包装器中。如果你需要的话,你可以写一个具体的class来包装一个特定的...

当你想按值传递事物时使用结构,当你想按引用传递它们时使用 类。

因为它们需要是值类型。点或矩形实际上不是对象。它们只是一堆数据。它们不会行为,它们只是存储您传递给它们的数据。你可以写

CGRect frame = ...
UIView *view1 = [[UIView alloc] initWithFrame:frame];

frame.origin = ...
UIView *view2 = [[UIView alloc] initWithFrame:frame];

而且效果很好。对于 类 的引用类型,您将在将框架移动到另一个原点时更改 view1 的框架。

[myRect intersectsWithRect: otherRect] 肯定会很好,实际上 Swift 允许这样做。结构和枚举等值类型可以包含 Swift 中的方法,这使得

成为可能

另外一个优点是运行速度。这些点和矩形主要用于渲染某些东西,因此需要比以往更快的操作。而不是分配一个对象,这将触发递归init,花费大量字节和cpu周期进行卧底工作,只是说"here I have 16 bytes which is 2 CGFloats of NSPoint struct"更快。这在将工作委托给 GPU 时也很有意义,GPU 对 OOP 和对象的方法一无所知,但需要在屏幕上无故障地绘制大量内容。

最重要的原因是性能 - Cocoa 的所有性能关键 API(图形、音频)都倾向于使用 C 结构和函数来实现此类目的。

原因是 C 编译器可以更轻松地优化这些。 C 函数可以比方法更便宜地调用,并且它们可以由编译器自动内联,从而完全消除函数调用开销。 C 结构分配在堆栈而不是堆上,这意味着处理大部分可以在寄存器或低级 CPU 缓存中完成,而不是调用主内存,这要慢数百倍。

Cocoa 方法是动态调度的。它们可能会被子类或 swizzling 覆盖,因此编译器无法内联它们,因为在运行时执行它们之前无法确定它们将做什么。 Cocoa 类 分配在堆上,因此它们必须是 allocated/deallocated,并且还有所有其他开销,例如引用计数。它们也可能最终散布在内存中,这意味着由于它们驻留的内存块必须在缓存中调入和调出缓存,因此可能会造成严重的性能损失。