如何更改类的元类

12

这种情况屡屡发生在我身上:我定义一个类,然后忘记把它设置成可调用的,或者它是一个 Gtk 组件类,因此需要声明它的元类。然而,一旦定义了元类,SBCL 就不允许我更改它(即使没有这个类的实例)。例如,执行以下代码:

(defclass foo ()
  ((slot-a)))

然后添加一个元类并重新评估:

(defclass foo ()
  ((slot-a))
  (:metaclass gobject:gobject-class))

导致错误的结果:

Cannot CHANGE-CLASS objects into CLASS metaobjects.
   [Condition of type SB-PCL::METAOBJECT-INITIALIZATION-VIOLATION]
See also:
  The Art of the Metaobject Protocol, CLASS [:initialization]

很不幸,我没有一本《元对象协议的艺术》的副本来核实它所说的内容。现在,我能想到的唯一方法是重新启动lisp,这可能会引起一些中断。

既然我很快意识到了错误,我不介意完全回避这个定义的类,只需将其删除即可。问题:

  • 如果我已经创建了该类的实例,是否有办法找到它们以使它们为null并获得GC处理?
  • 如何删除该类?类似于对函数使用fmakunbound的方式。

1个回答

12

很遗憾,我没有《元对象协议之艺术》的副本可以核对它所说的内容。

虽然我建议阅读这本书,但你可以在网上找到一些信息。例如请参见ENSURE-CLASS-USING-CLASS

如何删除类?

你可以使用(SETF FIND-CLASS)

(setf (find-class 'foo) nil)

或者,您可以使用高级的 slime 检查器。您需要调用 slime-inspect-definition 并指向类名。然后,您将看到该名称,当您选择它时,您将检查命名您的类的符号。然后,您可以看到如下内容:

It names the class FOO [remove]

如果提供的FOO只是一个类名,你可以使用更大的锤子:

(unintern 'foo)

如果我已经创建了该类的实例,是否有一种方法可以找到它们并将它们设置为 null 以便进行垃圾回收呢?

不,只有垃圾回收器具有全局视图,并且出于实际原因,它通常不保留关于谁引用特定对象(以及如何引用)的后向引用1。 除非你自己引入(弱)哈希表来存储它们,否则没有该类的所有实例的全局记录。但是,如果您已经记录了所有实例,您可以使用 CHANGE-CLASS 来更改它们的类。例如,您可以定义:

(defclass garbage () ())

当您更新对象引用时,您的对象中之前保存的任何引用都将被释放,垃圾回收机制有机会处理这些实例。当另一个对象引用“垃圾”的实例时,您可以对其进行更新。您可以将旧类的实例更改为新类的实例(名称相同,但类对象不同),而不是使用“垃圾”。请注意,CHANGE-CLASS是一个通用函数。


1. 实现可能会提供堆行者。例如,请参见Allegro CL中的堆行者


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