【问题标题】:Find where handle is stored in scope查找句柄在范围内的存储位置
【发布时间】:2016-07-18 09:19:15
【问题描述】:

MATLAB 句柄类对象是deleted when they go out of scope。我有可以在应用程序的不同部分重用的对象,但是当它们不再在任何地方使用时我想销毁它们。 MATLAB 的内置生命周期行为允许我执行此操作,而无需维护任何额外的全局列表来跟踪可能使用该对象的内容。

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

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

示例

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

classdef Yard < handle
    events
        RollCall
    end
end

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

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

最后是一个类,它通过显示一些文本来响应来自Kennel 对象的RollCall 事件:

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

我现在在范围内有两个Kennel 对象,其句柄在数组dogs 中的Dog 对象的属性中引用。调用 notify(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

如果原来的两个Dogs 被删除,那么 kennel 2 就会超出范围,但 kennel 1 仍然处于活动状态,因为它仍然被剩余的 Dog 引用:

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

但是,如果我在删除剩余的 Dog 之前在范围内的其他位置隐藏了 kennel 1 的附加句柄,那么它将保持活动状态:

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

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

【问题讨论】:

  • 您是在专门讨论图形句柄吗?如果是这样,这可能是一个不错的起点:A = findall(0);B = findobj;
  • 您是否尝试过显式破坏您的对象,而不是依靠 MATLAB 来为您完成? minimal reproducible example 会很好地解决这个问题。
  • @Dev-iL - 不,不幸的是,我只讨论用户定义类的对象的句柄。与其他句柄类对象的findall 类比将是理想的。
  • @excaza - 要决定显式销毁一个对象,我必须知道应用程序状态中的任何内容都不需要它。将生命周期附加到范围内的存在在许多意义上是一个完美的解决方案,并且要求整个代码库维护一些集中列表,然后负责显式对象销毁,这无异于为函数中的工作空间创建 MATLAB 框架的并行模型调用栈。我的问题涉及 MATLAB 的内置功能是否能够解决我的问题而不在如此低的级别上扩展它......

标签: matlab oop


【解决方案1】:

这是我经常处理的事情。您会看到,如果在任何其他范围内仍然存在对该对象的引用,则在一个范围内清除句柄对象变量不会触发析构函数。如果您显式调用 DogKennel 对象的析构函数,则您的计时器对象将引用无效对象。

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

我将为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

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

我经常使用handle对象,通常在包含GUI的图形句柄对象上设计GUIs 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 anyhandleobjects called in your code. I do this by setting the'DeleteFcn'`回调(可以是图形、uicontainer、uipanel等) .我包含这个示例是因为它说明了需要如何谨慎地维护对处理对象的引用。

函数 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

结束

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-07-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多