MATLAB 句柄类对象在超出范围时将被删除。我有一些可以在应用程序的不同部分重用的对象,但是当它们不再在任何地方使用时,我想销毁它们。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 yardn', 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 %dn', 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
如果删除了原来的两个Dog
,则狗窝 2 将超出范围,但狗窝 1 仍处于活动状态,因为它仍被其余Dog
引用:
>> dogs = dogs(3);
>> notify(Y,'RollCall')
Kennel 1 is in the yard
Rover is in kennel 1
但是,如果我在删除剩余Dog
之前将狗窝 1 的额外句柄隐藏在范围内的其他位置,那么它将保持活动状态:
>> t = timer('UserData',dogs(1).kennel);
>> clear dogs
>> notify(Y,'RollCall')
Kennel 1 is in the yard
问题是,如果我不知道此额外引用是在何时何地创建的,以及为什么没有将其删除,我该怎么做才能调试对象的存在?
了很多的事情。您会看到,如果任何其他作用域中仍有对该对象的引用,则清除一个作用域中的句柄对象变量将不会触发析构函数。如果显式调用了 Dog
和 Kennel
对象的析构函数,则计时器对象将具有对无效对象的引用。
在 Dog
的构造函数中创建了一个事件侦听器,其析构函数永远不会被调用,它保留对 Dog
实例的引用。
我会为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
对象。
我经常使用handle
对象,通常在包含 GUI 的图形句柄对象(可以是图形、uicontainer、uipanel 等)上设计 GUI 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
"DeleteFcn"回调。我包含此示例是因为它说明了需要如何仔细维护对处理对象的引用。
function 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 来清除这些对象。