最近有看到一个case,用户用Python自定义了一个节点,然后在自定义节点的构造函数 __init__ 中注册了一个事件回调函数,并且在相应的析构函数__del__ 中把对应的事件删除掉。代码看起来似乎没有什么问题,但是当用户执行的时候,发现该事件总是无法被删除掉。无论是重新建一个场景,或者是删除这个节点,甚至是卸载这个插件,该事件一直存在,没有被删除掉。
import sys
import maya.OpenMaya as OpenMaya
import maya.OpenMayaMPx as OpenMayaMPx
nodeName = "TestNode"
nodeId = OpenMaya.MTypeId(0x102fff)
class Test(OpenMayaMPx.MPxNode):
idCallback = None
def __init__(self):
OpenMayaMPx.MPxNode.__init__(self)
if self.idCallback==None :
self.idCallback = OpenMaya.MEventMessage.addEventCallback("SelectionChanged",self.callbackFunc)
def __del__(self):
try:
OpenMaya.MEventMessage.removeCallback(self.idCallback)
except:
print str(self.idCallback) + "already destroyed."
def callbackFunc(self,*args):
print "Called"
def compute(self, plug, dataBlock):
print "compute"
def nodeCreator():
nodePtr = OpenMayaMPx.asMPxPtr(Test())
return nodePtr
def nodeInitializer():
print "nodeInitializer"
def initializePlugin(mobject):
mplugin = OpenMayaMPx.MFnPlugin(mobject)
try:
mplugin.registerNode(nodeName, nodeId, nodeCreator, nodeInitializer)
except:
sys.stderr.write("Failed to register node: %s" % nodeName)
raise
def uninitializePlugin(mobject):
mplugin = OpenMayaMPx.MFnPlugin(mobject)
try:
mplugin.deregisterNode(nodeId)
except:
sys.stderr.write("Failed to deregister node: %s" % nodeName)
raise
仔细看了下问题并且在Maya环境中运行了下,发现问题在于析构函数__del__中,该函数在上述几种情况下一直无法被调用到。查了下Python析构函数的特性,这才发现了问题原因所在。
因为Python是用了自动垃圾回收(GC)来管理其内存的,也就是说,对象的真正析构时机是由GC来控制的。那么,当我们新建场景,删除节点,或者是卸载插件的时候,虽然表面上看该节点已经被删除了,但是其实际上很有可能还没有被GC回收,所以该析构函数也就一直没有被调用到。
如果想要让对象被删除掉,可以直接显式调用删除语句 del,这样才能强制GC去删除该对象。