为什么Objective-C对象必须动态分配?

17

为什么Objective-C对象必须要动态分配内存?为什么我必须将它定义为指向对象的指针,而不像在C++中可以在栈上创建对象? 谢谢。


2
因为Bjarne Stroustrup选择实现复制/按值语义,而Brad Cox/NeXTSTEP/Apple选择不这样做。 - user557219
3个回答

17

主要原因:不知道要保留多少堆栈大小。

现有的约定和用途也使得解除限制非常困难。

动态消息在这种情况下无关紧要,因为在初始化时设置正确的 'vtable' 很容易。

在 C++ 中,堆栈对象的大小始终是已知的(如果错误,你知道会发生什么)。Objc 的分配/初始化序列可以返回任何一个不同大小的类型(它本质上是一个工厂),或者完全没有返回值。

大小还可以在运行时变化(例如,你可以通过运行时向类添加字段)。

更新 1

我对此很好奇,所以我做了一个小测试程序作为概念证明。

我能够实现一个简单的基于堆栈的 Objc 类层次结构,它也实现了相当一部分 NSObject 的接口 - 当然,省略了与引用计数和动态分配接口无关的部分。无论如何,我的简单类层次结构与 NSObject 类或协议并不完全兼容,因此,它不应该在期望 NSObject 类型的地方使用,理由很明显。因此,如果你确实想要基于堆栈的 Objc 对象,这是可能的(而且不是特别困难)。

你不需要像 C++ 一样做任何不同的事情来保留堆栈空间。在某些领域中,堆栈大小仍然是限制因素(考虑工厂方法、类集群等)。

还有一些运行时功能默认情况下将无法工作。最好的例子是动态添加实例变量的能力。如果需要,你可以实现此功能。我没有费心思去做那个练习。

当然,基础接口可能会有几个偏差 - 我为了好玩加了一种偏差,即添加交换活动对象的实现(类型)的能力。

玩得开心

更新 2

事实证明,GCC接受了我编写的概念验证。不幸的是,由于在保留正确尺寸时可能遇到的问题和危险(考虑到语言的动态特性...),这在clang中已被禁止。例如:clang禁止sizeof(NSObject)。哦,好吧。


堆栈对象的一个缺点是没有所有权的概念 - 它们必须在块/函数/堆栈范围的末尾被销毁。这意味着堆栈范围之外的任何东西都需要复制堆栈对象。 - user557219
我更喜欢称其为另一种(自动)所有权模型。所有权已经被很好地定义了,只是与动态模型不同。在我编写的C++程序中,最常使用本地创建的自动对象(而不是通过分配器进行分配),这非常重要。当两种模型都可用,并且需要返回分配的对象时,可以通过在需要时创建动态对象来绕过您提到的临时对象:return new MON::t_string(number);。无论好坏,C++都有更多的东西。 - justin
如果有人要实现这个功能,最好通过指针或引用传递 - 但在c++中也是如此,因为堆栈objc对象仍应被假定为多态的(除非有人想在实现过程中破坏许多现有程序)。无论如何,对象仍需要消息传递的内部脚手架。除此之外,在c++中的问题称为切片。其余问题可以通过使用接口(例如方法)而不是直接客户端访问ivars来解决。 - justin
2
如果您还没有阅读过它:Friday Q&A 2010-01-15: Stack and Heap Objects in Objective-C - user557219
1
我能够让clang使用class_getInstanceSize()、alloca()和object_setClass()在堆栈上创建对象。这样可以正确获取动态大小。但是类簇仍然是一个问题。https://gist.github.com/iamleeg/5290797 - user23743
显示剩余5条评论

2

Objective-C是一种动态语言,这意味着它的所有内容都可以在运行时更改。对象的类对象仅在从可执行文件中加载时创建,并且可以通过分类进行更改。此外,运行时可以为属性实例化变量。由于编译后对象的很多方面都可以更改,因此只有在使用时才能创建它。


1
这是因为Objective-C使用动态或晚绑定的方式。与C++不同,你总是可以选择通过对象调用类的函数,或者通过相同类或超类的指针调用函数。在后一种情况下,需要多态性。
然而,在Objective-C中,始终存在在运行时确定正确函数的能力。不同之处在于,例如在C++中,编译器必须确保所使用的函数存在,而在Objective-C中,编译器并不真正关心,只有运行时系统决定。

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