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

时间:2012-03-04 23:24:20

标签: oop matlab destructor handle

我有两个班级,PlantGeneratorGenerator创建了一个向量,并通过notify()监听Plant进行广播。 classdef如下。请注意,我没有包含实际的数据生成方法,因为它对我的问题无关紧要。

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?

每次我的析构函数运行都非常重要。这里发生了什么,我怎样才能让析构函数正常运行? (我可能只是完全删除了侦听器,并直接从Plant.ListenerCallback()实例调用Generator,如果这不成功的话。)

编辑:看起来当我执行clear G时,变量G已从工作区中删除 - 但Generator对象仍然存在于{{1}中}}。这就是没有调用析构函数的原因。所以我想删除P.Listener.Source没有办法摆脱P.Listener ..有什么方法可以让我做我想做的事情,或者我只是被卡住了?

2 个答案:

答案 0 :(得分:1)

为什么析构函数会在如此奇怪的时刻被调用?

clear G #% DESTRUCTOR NOT CALLED??

G

的听众中仍然有P的引用
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',varargin{i}));
   else
      evalin('caller',sprintf('builtin(''clear'',''%s'');',varargin{i});
   end
end

答案 1 :(得分:1)

也许我正在复活一个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));

匿名函数中没有硬编码引用。侦听器回调的第一个参数始终是从中调用的对象,但是您可以动态获取它,而不是在匿名函数中对对象引用进行硬编码。

希望这对你有一定价值!