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?
Example
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 dogs
. Calling 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 Dog
s are deleted then kennel 2 goes out of scope but kennel 1 remains active since it is still referenced by the remaining Dog
:
>> 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?
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
Dog
andKennel
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 ofDog
.I would implement a delete function for
Dog
andKennel
which called the delete function for the listeners. Here is a coding pattern I use a lot.In the example you gave, you created a
timer
object which also maintains a reference to yourdogs
object. Clearingdogs
does not eliminate this reference. Neither would clearing thetimer
object. You must explicitly deletetimer
objects.I use
handle
objects a lot, and usually design 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
handleobjects 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.function dispGUI(handleOBJ,hg)
end
Another issue I have noticed with
handle
class objects, is that if you modify and save theirclassdef
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.