查找句柄在作用域中的存储位置

Find where handle is stored in scope

提问人:Will 提问时间:7/18/2016 最后编辑:Will 更新时间:9/18/2016 访问量:480

问:

当 MATLAB 句柄类对象超出范围时,它们将被删除。我有一些对象可以在应用程序的不同部分重用,但是当它们在任何地方不再使用时,我想销毁它们。借助 MATLAB 内置的生命周期行为,我无需维护任何额外的全局列表即可完成此操作,从而跟踪可能正在使用该对象的内容。

但是,我遇到的情况是,我认为应该超出范围的对象仍在触发事件侦听器回调,这些回调作为对象析构函数的一部分被删除。我知道我认为这个对象的最后一个句柄应该存储在哪里,果然,当我检查那里时,该句柄已被清除。因此,在其他地方的范围内必须有此句柄的实例。

我的应用程序是一个复杂的对象网络,这些对象存储为其他对象的属性。我能做些什么来帮助跟踪此对象的句柄在范围内的存储位置吗?

首先设置一个带有要侦听的事件的句柄类:

classdef Yard < handle
    events
        RollCall
    end
end

然后是一个句柄类,它通过显示一些文本,然后通知自己的事件来对来自对象的事件做出反应:RollCallYard

classdef Kennel < handle
    properties
        id
        yardListener
    end

    events
        RollCall
    end

    methods
        function obj = Kennel(yard,id)
            obj.yardListener = event.listener(yard,'RollCall',@obj.Report);
            obj.id = id;
        end

        function Report(obj,~,~)
            fprintf('Kennel %d is in the yard\n', obj.id);
            notify(obj,'RollCall');
        end
    end
end

最后是一个类,它通过显示一些文本来对来自对象的事件做出反应:RollCallKennel

classdef Dog
    properties
        name
        kennel
        kennelListener
    end

    methods
        function obj = Dog(name,kennel)
            obj.name = name;
            obj.kennel = kennel;
            obj.kennelListener = event.listener(kennel,'RollCall',@obj.Report);
        end

        function Report(obj,kennel,~)
            fprintf('%s is in kennel %d\n', obj.name, kennel.id);
        end
    end 
end

现在,将这些类的一些实例添加到工作区中:

Y = Yard;
% Construct two Dog objects, in each case constructing a new Kennel object to pass to the constructor
dogs = [Dog('Fido',Kennel(Y,1)) Dog('Rex',Kennel(Y,2))];
% Construct a third Dog, reusing the Kennel object assigned to dog(1)
dogs(3) = Dog('Rover',dogs(1).kennel);

我现在在作用域中有两个对象,在数组中对象的属性中引用了句柄。调用将产生以下输出:KennelDogdogsnotify(Y,'RollCall')

Kennel 2 is in the yard
Rex is in kennel 2
Kennel 1 is in the yard
Rover is in kennel 1
Fido is in kennel 1

如果删除了原来的两个 s,则 kennel 2 超出范围,但 kennel 1 仍然处于活动状态,因为它仍被其余的 s 引用:DogDog

>> dogs = dogs(3);
>> notify(Y,'RollCall')
Kennel 1 is in the yard
Rover is in kennel 1

但是,如果我在删除其余句柄之前将 1 号犬舍的附加句柄隐藏在范围内的其他地方,那么它将保持活动状态:Dog

>> t = timer('UserData',dogs(1).kennel);
>> clear dogs
>> notify(Y,'RollCall')
Kennel 1 is in the yard

问题是,如果我不知道这个额外的引用是在何时何地创建的,以及为什么它没有被删除,我能做些什么来调试对象的存在?

MATLAB 哎呀

评论

0赞 Dev-iL 7/19/2016
您是在专门谈论图形句柄吗?如果是这样,这可能是一个不错的起点:或 .A = findall(0);B = findobj;
0赞 sco1 7/19/2016
您是否尝试过显式地销毁对象,而不是依赖 MATLAB 为您完成?这个问题可以通过一个最小的可重复的例子来很好地解决。
0赞 Will 7/20/2016
@Dev-iL - 不,不幸的是,我只谈论用户定义类对象的句柄。对其他句柄类对象进行类比将是理想的。findall
0赞 Will 7/20/2016
@excaza - 要决定显式销毁一个对象,我必须知道应用程序状态中的任何内容都不需要它。从许多意义上说,将生命周期附加到范围内的存在是一个完美的解决方案,并且要求整个代码库维护一些集中的列表,然后负责显式对象销毁,这无异于在函数调用堆栈中为工作区创建MATLAB框架的并行模型。我的问题是关于MATLAB的内置功能是否能够解决我的问题,而无需将其扩展到如此低的水平......
0赞 Will 7/20/2016
...@excaza这确实可以用 MCV 更好地解释,但是,我将在接下来的几天内尝试将其放在一起。

答:

2赞 dsgrnt 9/18/2016 #1

这是我处理过很多的事情。您会看到,如果在任何其他作用域中仍存在对对象的引用,则清除一个作用域中的句柄对象变量不会触发析构函数。如果显式调用了 and 对象的析构函数,则计时器对象将具有对无效对象的引用。DogKennel

在构造函数中,创建了一个事件侦听器,其析构函数永远不会被调用,它保留了对 的实例的引用。DogDog

我将实现一个删除函数,该函数调用 侦听器的 Delete 函数。这是我经常使用的编码模式。DogKennel

classdef MyClass < handle
   properties 
      listener % listeners are themselves handle objects
   end
   methods
      %% Class constructor creates a listener 
      %% Class methods
      function delete(obj)
         % do this for all handle object properties
         % Also, avoid calling constructors for handle objects in
         % the properties block. That can create references to handle
         % objects which can only be cleared by a restart
         if ishandle(obj.listener)
            delete(obj.listener)
         end
      end
   end
end

在您给出的示例中,您创建了一个对象,该对象还维护了对对象的引用。清除不会消除此引用。清除对象也不会。您必须显式删除对象。timerdogsdogstimertimer

我经常使用对象,通常在包含 GUI 的图形句柄对象(可以是 figure、uicontainer、uipanel 等)上设计 GUIhandle'DeleteFcn'' 回调。我之所以包含此示例,是因为它说明了需要如何仔细维护对处理对象的引用。handles to go with them (aside, never, never, never use GUIDE for this. It is the worst thing ever). You have to be sure to clear all references to anyobjects called in your code. I do this by setting the

函数 dispGUI(handleOBJ,hg)

  %% build gui in hg
  set(hg,'DeleteFcn',@deleteCallBack)
  h  = uicontrol(hg,'Callback',@myCallback)
  function myCallback(h,varargin)
     % So now there is a reference to handleOBJ
     % It will persist forever, even if cleared in the caller's
     % workspace, thus we clear it when the containing graphics 
     % object delete callback is triggered.
     if isvalid(handleOBJ)
        set(h,'string','valid')
     else
        set(h,'string','invalid')
     end
  end


  function deleteCallBack(varargin)
     % calling clear on handleOBJ clears it from the scope of the 
     % GUI function
     % also call clear on the containing function
     clear handleOBJ
     clear(mfilename)
  end

结束

我注意到类对象的另一个问题是,如果您在对这些对象进行实时引用时修改并保存它们的文件,您偶尔会遇到对对象的引用只能通过重置 Matlab 来清除的情况。handleclassdef