查找句柄存储在范围内的位置

Wil*_*ill 12 oop matlab

当MATLAB句柄类对象超出范围时,它们将删除.我有可以在应用程序的不同部分重用的对象,但是当它们不再在任何地方使用时我想破坏它们.MATLAB内置的生命周期行为允许我在不维护任何其他全局列表的情况下执行此操作,以跟踪可能正在使用该对象的内容.

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

我的应用程序是一个复杂的对象网络,存储为其他对象的属性.我有什么办法可以帮助追踪存储此对象句柄的范围吗?

首先设置一个句柄类,其中包含要监听的事件:

classdef Yard < handle
    events
        RollCall
    end
end
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

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

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
Run Code Online (Sandbox Code Playgroud)

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

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);
Run Code Online (Sandbox Code Playgroud)

我现在Kennel在范围内有两个对象,Dog在数组中的对象的属性中引用了句柄dogs.调用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
Run Code Online (Sandbox Code Playgroud)

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

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

但是,如果我在删除剩余部分之前在范围内的其他位置隐藏了一个额外的句柄,Dog那么它将保持活动状态:

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

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

dsg*_*rnt 2

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

在 for 的构造函数中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
Run Code Online (Sandbox Code Playgroud)

在您给出的示例中,您创建了一个timer对象,该对象还维护对您的对象的引用dogs。清算dogs并不能消除该参考。两者都不会清除该timer对象。您必须显式删除timer对象。

handle经常使用对象,通常在包含 GUI 的图形句柄对象(可以是图形、uicontainer、uipanel 等)上设计 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 any句柄'DeleteFcn'` 回调。objects 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
Run Code Online (Sandbox Code Playgroud)

结尾

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