Find where handle is stored in scope
1268 Репутация автора
MATLAB handle class objects are deleted when they go out of scope. I have objects that can be reused in different parts of an application, but which I want to destroy when they are no longer used anywhere. MATLAB's built in lifecycle behaviour allows me to do this without maintaining any additional global list to keep track of what might be using that object.
However I have a situation where an object I think should have gone out of scope is still firing event listener callbacks that are deleted as part of the object's destructor. I know where I think the last handle to this object in existence should have been stored, and sure enough when I check there that handle has been cleared. So there must be instances of this handle in scope somewhere else.
My application is a complex network of objects stored as properties of other objects. Is there anything I can do to help track down where in scope the handle to this object is being stored?
First set up a handle class with an event to listen to:
classdef Yard < handle events RollCall end end
Then a handle class that reacts to
RollCall events from a
Yard object by displaying some text and then notifying its own event:
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
And finally a class that reacts to
RollCall events from a
Kennel object by displaying some text:
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
Now add some instances of these classes to the workspace:
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);
I now have two
Kennel objects in scope, with handles referenced in the properties of the
Dog objects in the array
notify(Y,'RollCall') produces the following output:
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
If the original two
Dogs are deleted then kennel 2 goes out of scope but kennel 1 remains active since it is still referenced by the remaining
>> dogs = dogs(3); >> notify(Y,'RollCall')
Kennel 1 is in the yard Rover is in kennel 1
However if I hide an additional handle to kennel 1 somewhere else in scope before deleting the remaining
Dog then it will remain active:
>> t = timer('UserData',dogs(1).kennel); >> clear dogs >> notify(Y,'RollCall')
Kennel 1 is in the yard
The question is if I don't know where or when this extra reference was created and why it hasn't been deleted, what can I do to debug the existence of the object?Автор: Will Источник Размещён: 18.07.2016 09:19
348 Репутация автора
This is something I have dealt with a lot. You see, clearing the handle object variable in one scope will not trigger the destructor if there is still a reference to the object in any other scope. If you explicitly called the destructor for the
Kennel objects, your timer object would have a reference to an invalid object.
In the constructor for
Dog there is an event listener created, whose destructor is never called which retains a reference to an instance of
I would implement a delete function for
Kennel which called
the delete function for the listeners. Here is a coding pattern I use a lot.
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
In the example you gave, you created a
timer object which also maintains a reference to your
dogs object. Clearing
dogs does not eliminate this reference. Neither would clearing the
timer object. You must explicitly delete
handle objects a lot, and usually design 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 anyhandle
objects called in your code. I do this by setting the'DeleteFcn'` callback on the graphics handle object that contains the GUI (could be a figure, uicontainer, uipanel, etc.). I include this example because it illustrates how carefully references to handle objects need to be maintained.
%% 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
Another issue I have noticed with
handle class objects, is that if you modify and save their
classdef file while there are live references to these objects you can occasionally get into a situation where there are references to the object that you can only clear by resetting Matlab.