我想知道一个类(或对象)的生命周期是什么,我的意思是,当它进入内存并活着回答消息直到最终从内存中删除时。
我的假设是:
好的,所以一个对象从它的实例化开始一直活着,直到垃圾收集杀死它或者你向它发送一条销毁消息(或者它的类)
但是,您如何实例化一个类以使其活跃?您不能向某种元类发送 NEW 消息(如果是,那么实例化元类的元元类是什么?)那么,默认情况下类是活动的吗?加载图像时,smalltalk 库中的每个类都会加载到内存中吗?真的有很多类随时都在内存中吗?如果是这样,我如何才能在特定时刻查看内存中的类和对象?
正如 Leandro 和 Bert 所描述的,您可以发送new到元类。这创建的循环引用有点难以思考,但它们代表了设计和实现的“简单”和“优雅”(旁观者眼中的美!)。我在下面附上了一张图,展示了这是如何工作的。特别注意Metaclass和之间的循环Metaclass class。
Metaclass allInstances.另请注意,虽然大多数 Smalltalks 都有 ClassBuilder,但并非所有都这样做(GemStone 没有),并且其属性和行为可以折叠到 Behavior 中。
补充问题:
每个 Smalltalk 都有一个ClassBuilder类,其实例的目的是创建新类。一个ClassBuilder创建第一个做到这一点Metaclass的大约要构建的类,发送该类消息#new对Metaclass。然后它是Metaclass刚刚创建的那个,它使用 primive 创建了它的唯一实例'New'。
使用 a 的原因ClassBuilder是类不仅被创建,它们还可以被修改。例如,一个类可以用另一个类改变它的超类,或者保留它但改变它的实例的形状或它自己的形状。
该ClassBuilder负责所有的细节,并且还负责重新编译实例或类方法的形状发生变化的时候。请注意,一个类的更改将影响其所有子类(如果有)。因此,该类所需的所有任务也需要在子类中进行。
此外,需要安装新的类,因为它们必须包含在 Smalltalk 字典中,以便使它们既可访问又有效。
最后请注意,在形状发生变化的情况下(这可能是显式的,也可能是将超类更改为另一个的结果),必须迁移该类的现有实例以符合新的形状。所有这些任务使得在幕后运行的过程变得非常重要。但是,从发送类定义消息一直到'New' 原语和现有实例的迁移,您都可以遵循它。
关于类的垃圾收集方式,嗯,这里没有什么特别的。Smalltalk 没有任何销毁消息。不再被引用的对象准备好被收集。甚至可能仍然被引用的对象也可以被收集,因为两个对象相互引用但没有从“外部”引用,即(可能间接)从任何根引用。
类没有(容易)GCed 的原因是因为它们是从Smalltalk字典中引用的,字典是系统的根源之一。当然,您可以手动删除一个类,这意味着它将从Smalltalk字典中删除。在这些情况下,环境将帮助程序员查找是否存在对Association将类名绑定到其他类的类对象的引用。该环境还将帮助程序员检查将要删除的类是否有实例或子类。此外,由于每个类都知道它的子类,因此从那里引用类,因此作为删除类的过程的一部分,超类需要从其子类列表中删除该类。
附录(来自下面的评论)
问:谁创建了ClassBuilder类?
答:这是每个 Smalltalk 方言都解决的(许多)引导问题之一。任何 Smalltalk 都是从内核中诞生的,即,一组最小的类和实例,只为创建新类提供足够的支持。根据方言,ClassBuilder可能属于或不属于内核。如果没有,那么您仍然可以手动创建类,因为唯一需要的是能够将#new消息发送到Metaclass. 但是请注意Metaclass(类)需要在内核中才能工作。此外,当您保存图像时,所有对象(包括类和方法,它们也是对象)都会保存到磁盘。之后,当您在内存中加载图像时,相同的对象在那里,并且在保存时运行的任何正在运行的进程(也是 Smalltalk 对象)将在保存操作发生时从它们被中断的地方恢复。还要注意,没有对象被“实例化”,它们只是在复制(+ 可能的重定位)原始操作中从磁盘加载到内存中。因此,#new当您加载图像时不涉及,因为对象已经存在。
问:如何ClassBuilder实例化自身?
A.您可以将其视为手工构建然后保存在图像中。因此,下次加载图像ClassBuilder(类)时将可用。实际上, 的复杂性ClassBuilder可能需要多次迭代。系统/运行时程序员可以从创建它的一个非常简单的版本开始,小心处理,然后使用这个初始类在进一步迭代中完善自身。
问:有没有 Metaclass为每类?
答:是的。
问:有没有什么书可以了解 Smalltalk 的内部结构和实现?
A. Smalltalk-80 语言及其实现(你可以在网上找到它)