如何改变班级的元类

mob*_*eng 9 sbcl common-lisp clos

这种情况一次又一次地发生在我身上:我定义了这个类并忘记了我想要它的功能,或者它是,例如,Gtk小部件类,因此它的元类需要说明.一旦它被定义,但是,SBCL不会让我改变了我的元类(即使没有这个类的实例).例如,评估

(defclass foo ()
  ((slot-a)))
Run Code Online (Sandbox Code Playgroud)

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

(defclass foo ()
  ((slot-a))
  (:metaclass gobject:gobject-class))
Run Code Online (Sandbox Code Playgroud)

导致错误:

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]
Run Code Online (Sandbox Code Playgroud)

不幸的是,我没有"元对象协议的艺术"的副本来检查它的内容.现在我唯一可以解决的方法是重启lisp,这可能会造成很大的破坏性.

由于我很快意识到错误,我不介意通过删除它完全避开已定义的类.问题:

  • 如果我已经创建了类的实例,有没有办法找到它们来使它们无效并让它们获得GCed?
  • 如何删除课程?像fmakunbound功能一样的东西.

cor*_*ump 10

不幸的是,我没有"元对象协议的艺术"的副本来检查它的内容.

即使我推荐阅读这本书,你也可以在网上找到一些信息.例如,参见ENSURE-CLASS-USING-CLASS.

如何删除课程?

你可以使用(SETF FIND-CLASS):

(setf (find-class 'foo) nil)
Run Code Online (Sandbox Code Playgroud)

或者,您可以使用花式粘液检查员.slime-inspect-defintion在指向类的名称时调用.然后,你会看到名字.选择它时,检查命名类的符号.然后,你可以看到类似的东西:

It names the class FOO [remove]
Run Code Online (Sandbox Code Playgroud)

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

(unintern 'foo)
Run Code Online (Sandbox Code Playgroud)

如果我已经创建了类的实例,有没有办法找到它们来使它们无效并让它们获得GCed?

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

(defclass garbage () ())
Run Code Online (Sandbox Code Playgroud)

...将释放您对象中以前保存的任何引用,并且GC有机会处理引用的实例对象.当另一个对象引用'garbage的实例时,你可以更新它.而不是使用"的垃圾,你或许可以改变旧的类的实例,以新类(名字是一样的,但类对象是不同的).另请注意,这CHANGE-CLASS是一个通用函数.


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