MATLAB - 涉及侦听器时对象析构函数不运行

Posted

技术标签:

【中文标题】MATLAB - 涉及侦听器时对象析构函数不运行【英文标题】:MATLAB - object destructor not running when listeners are involved 【发布时间】:2012-03-04 23:24:19 【问题描述】:

我有两个班级,PlantGeneratorGenerator 创建一个向量并通过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 的人都不知道 deleteclear 之间的区别,所以这可能会让人非常困惑。 是的,我明白了。您可能希望使用此信息更新您的问题,因为这不是析构函数的问题,而是对象上的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 - 涉及侦听器时对象析构函数不运行的主要内容,如果未能解决你的问题,请参考以下文章

静态对象成员会在所属类的析构函数被调用时自动析构吗?

堆栈上的对象被覆盖时不调用析构函数

当类没有析构函数时,智能指针或作用域指针会删除对象吗

vc9 和 gcc 之间的不同析构函数行为

C++ 虚拟析构函数 (virtual destructor)

构造函数拷贝构造函数析构函数