MATLAB句柄类对象是deleted when they go out of scope。我有可以在应用程序的不同部分重用的对象,但是当它们不再在任何地方使用时我想破坏它们。 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
如果原始的两个Dog
被删除,那么狗舍2就会超出范围,但是狗舍1仍然有效,因为它仍被剩余的Dog
引用:
>> dogs = dogs(3);
>> notify(Y,'RollCall')
Kennel 1 is in the yard
Rover is in kennel 1
但是,如果我在删除剩余的Dog
之前在范围内的其他位置隐藏了一个额外的句柄,那么它将保持活动状态:
>> t = timer('UserData',dogs(1).kennel);
>> clear dogs
>> notify(Y,'RollCall')
Kennel 1 is in the yard
问题是,如果我不知道创建这个额外引用的位置和时间以及为什么没有删除它,我该怎么做来调试对象的存在?
答案 0 :(得分:1)
这是我经常处理的事情。您会看到,如果在任何其他范围内仍然存在对该对象的引用,则清除一个范围中的句柄对象变量将不会触发析构函数。如果您为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的图形句柄对象上设计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
&#39; DeleteFcn&#39;`回调是一个人物,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来清除对象。