MATLAB - деструктор объекта не работает, когда задействованы слушатели - PullRequest
2 голосов
/ 05 марта 2012

У меня есть два класса, Plant и Generator.Generator создает вектор и передает его через notify(), который Plant прослушивает.Classdefs ниже.Обратите внимание, что я не включил реальный метод генерации данных, потому что он не имеет отношения к моему вопросу.

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 остается в P.Listener.Source.Вот почему деструктор не вызывается.Поэтому я думаю, что нет способа избавиться от P.Listener, удалив G .., есть ли способ заставить это делать то, что я хочу, или я просто застрял?

Ответы [ 2 ]

1 голос
/ 22 мая 2014

Возможно, я воскрешаю двухлетний вопрос, но ...

Матлаб хочет вызвать деструктор в чистом виде;проблема в том, как вы определили своего слушателя.Вы определили это как:

 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));

, то в анонимной функции нет жестко закодированных ссылок.Первым аргументом обратного вызова слушателя всегда является объект, из которого он был вызван, но вы получаете его на лету, а не жестко кодируете ссылку на объект в анонимной функции.

Надеюсь, это все еще имеет для вас какое-то значение!

1 голос
/ 05 марта 2012

Почему деструктор вызывается в такие странные времена?

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
...