提问人:Will 提问时间:7/18/2016 最后编辑:Will 更新时间:9/18/2016 访问量:480
查找句柄在作用域中的存储位置
Find where handle is stored in scope
问:
当 MATLAB 句柄类对象超出范围时,它们将被删除。我有一些对象可以在应用程序的不同部分重用,但是当它们在任何地方不再使用时,我想销毁它们。借助 MATLAB 内置的生命周期行为,我无需维护任何额外的全局列表即可完成此操作,从而跟踪可能正在使用该对象的内容。
但是,我遇到的情况是,我认为应该超出范围的对象仍在触发事件侦听器回调,这些回调作为对象析构函数的一部分被删除。我知道我认为这个对象的最后一个句柄应该存储在哪里,果然,当我检查那里时,该句柄已被清除。因此,在其他地方的范围内必须有此句柄的实例。
我的应用程序是一个复杂的对象网络,这些对象存储为其他对象的属性。我能做些什么来帮助跟踪此对象的句柄在范围内的存储位置吗?
例
首先设置一个带有要侦听的事件的句柄类:
classdef Yard < handle
events
RollCall
end
end
然后是一个句柄类,它通过显示一些文本,然后通知自己的事件来对来自对象的事件做出反应: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
最后是一个类,它通过显示一些文本来对来自对象的事件做出反应: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
现在,将这些类的一些实例添加到工作区中:
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
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
如果删除了原来的两个 s,则 kennel 2 超出范围,但 kennel 1 仍然处于活动状态,因为它仍被其余的 s 引用:Dog
Dog
>> 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
问题是,如果我不知道这个额外的引用是在何时何地创建的,以及为什么它没有被删除,我能做些什么来调试对象的存在?
答:
这是我处理过很多的事情。您会看到,如果在任何其他作用域中仍存在对对象的引用,则清除一个作用域中的句柄对象变量不会触发析构函数。如果显式调用了 and 对象的析构函数,则计时器对象将具有对无效对象的引用。Dog
Kennel
在构造函数中,创建了一个事件侦听器,其析构函数永远不会被调用,它保留了对 的实例的引用。Dog
Dog
我将实现一个删除函数,该函数调用
侦听器的 Delete 函数。这是我经常使用的编码模式。Dog
Kennel
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
我经常使用对象,通常在包含 GUI 的图形句柄对象(可以是 figure、uicontainer、uipanel 等)上设计 GUIhandle'DeleteFcn'' 回调。我之所以包含此示例,是因为它说明了需要如何仔细维护对处理对象的引用。handle
s 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
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
结束
我注意到类对象的另一个问题是,如果您在对这些对象进行实时引用时修改并保存它们的文件,您偶尔会遇到对对象的引用只能通过重置 Matlab 来清除的情况。handle
classdef
评论
A = findall(0);
B = findobj;
findall