MATLAB - 涉及侦听器时对象析构函数不运行
Posted
技术标签:
【中文标题】MATLAB - 涉及侦听器时对象析构函数不运行【英文标题】:MATLAB - object destructor not running when listeners are involved 【发布时间】:2012-03-04 23:24:19 【问题描述】:我有两个班级,Plant
和 Generator
。 Generator
创建一个向量并通过notify()
广播它,Plant
监听它。类定义如下。请注意,我没有包含实际的数据生成方法,因为它与我的问题无关。
classdef Plant < handle
properties
Listener
end
methods
function ListenerCallback(obj, data)
#% Perform an operation on data
end
end
end
classdef Generator < handle
properties
plant
end
events
newSignal
end
methods
function obj = Generator(plant)
obj.plant = plant;
obj.plant.Listener = addlistener(obj, 'newSignal', ...
@(src, data) obj.plant.ListenerCallback(data));
end
function delete(obj)
delete(obj.plant.Listener);
disp('Generator instance deleted');
end
end
end
我注意到Generator
析构函数的行为非常奇怪:我第一次创建然后删除Generator
实例时,它直到我第二次创建Generator
实例时才运行析构函数。这是一个例子:
>> P = Plant
P =
Plant handle
Properties:
Listener: []
Methods, Events, Superclasses
>> G = Generator(P)
G =
Generator handle
Properties:
plant: [1x1 Plant]
Methods, Events, Superclasses
>> clear G #% DESTRUCTOR NOT CALLED??
>> G = Generator(P)
Generator instance deleted #% why is the destructor run now?
G =
Generator handle
Properties:
plant: [1x1 Plant]
Methods, Events, Superclasses
>> clear G
Generator instance deleted #% and why is the destructor run properly now?
我的析构函数每次都运行非常重要。这里发生了什么,我怎样才能让析构函数正常运行? (如果这不起作用,我可能会完全删除侦听器并直接从 Generator
实例中调用 Plant.ListenerCallback()
。)
编辑:看起来当我执行 clear G
时,变量 G
已从工作区中删除 - 但 Generator
对象仍然存在于 P.Listener.Source
中。这就是为什么不调用析构函数的原因。所以我想没有办法通过删除G
来摆脱P.Listener
.. 有没有办法让它做我想做的事情还是我只是卡住了?
【问题讨论】:
只尝试delete G; clear G
而不是clear G
?从文档中,“您可以清除图形或其他对象的句柄,但这不会删除对象本身。使用 delete 删除对象和文件。删除对象不会删除用于存储其的变量(如果有)句柄。”
@tmpearce - 确实有效。我希望只使用clear G
,因为这段代码是更大代码库的一部分。与我一起工作的大多数使用 MATLAB 的人都不知道 delete
和 clear
之间的区别,所以这可能会让人非常困惑。
是的,我明白了。您可能希望使用此信息更新您的问题,因为这不是析构函数的问题,而是对象上的clear
。
另外,请查看上一个问题了解更多信息:***.com/questions/7236649/…
@strictlyrude27:请参阅下面我编辑的答案。简短回答:facepalm
【参考方案1】:
为什么在这么奇怪的时候调用析构函数?
clear G #% DESTRUCTOR NOT CALLED??
P
的监听器中还有对G
的引用
G = Generator(P)
Generator instance deleted #% why is the destructor run now?
当新的Generator
被实例化时,监听器被覆盖。这调用了Generator
的第一个实例的析构函数,因为不再有任何对它的引用。
G =
Generator handle
Properties:
plant: [1x1 Plant]
Methods, Events, Superclasses
>> clear G
Generator instance deleted #% and why is the destructor run properly now?
让我们再看看上一步发生了什么:(1)plant
的监听器被新的Generator
覆盖。 (2) 这会调用第一个Generator
的析构函数。 (3) 析构函数清除plant
(!!!) 的监听器 (4) 工作区中的G
现在是新的Generator
的最后一个剩余实例。因此,clear G
调用类析构函数。
一种允许您使用clear
而不是delete
的不太好的方法是重载clear
命令
function clear(varargin)
%# check for Generator objects
isGenerator = cellfun(@(x)evalin('caller','isa(x,''Generator'');'),varargin);
%# I loop here b/c I don't have the time to carefully construct
%# the evalin commands
%# Generator gets deleted, everybody else gets cleared
for i=1:nargin
if isGenerator(i)
evalin('caller',sprintf('delete %s',varargini));
else
evalin('caller',sprintf('builtin(''clear'',''%s'');',varargini);
end
end
【讨论】:
很好的解释。丑陋但简单的解决方法。一如既往地感谢,先生。【参考方案2】:也许我正在复活一个 2 年前的问题,但是......
Matlab 想要在清除时调用析构函数;问题在于你如何定义你的听众。您将其定义为:
obj.plant.Listener = addlistener(obj, 'newSignal', ...
@(src, data) obj.plant.ListenerCallback(data));
这样做,您创建了一个匿名函数,该函数具有对 obj 的硬编码引用。当 obj 在其他地方超出范围时(例如,通过基础工作区中的清除),它仍然继续存在于您的匿名函数中。如果您改为定义:
obj.plant.Listener = addlistener(obj, 'newSignal', ...
@(src, data) src.plant.ListenerCallback(data));
匿名函数中没有硬编码引用。侦听器回调的第一个参数始终是调用它的对象,但您可以即时获取它,而不是在匿名函数中硬编码对象引用。
希望这对您仍然有价值!
【讨论】:
这对于类事件来说是一个很好的解决方案,但不幸的是对于独立于类的定时器回调或串行事件来说不是。以上是关于MATLAB - 涉及侦听器时对象析构函数不运行的主要内容,如果未能解决你的问题,请参考以下文章