This is somewhat related to this question, but not quite.
I have two classes, FunctionWrapper
and TimerWrapper
:
classdef FunctionWrapper < handle
methods
function Fcn(obj)
disp('FunctionWrapper.Fcn was called!');
end
end
end
classdef TimerWrapper < handle
properties
Timer
end
methods
function obj = TimerWrapper(other_object)
obj.Timer = timer;
set(obj.Timer, 'Period', 1);
set(obj.Timer, 'ExecutionMode', 'fixedSpacing');
set(obj.Timer, 'TimerFcn', @(event, data) other_object.Fcn);
end
function start(obj)
start(obj.Timer);
end
function stop(obj)
stop(obj.Timer);
end
function delete(obj)
disp('destructor called!');
delete(obj.Timer);
end
end
end
Say I execute the following code in the Command Window:
>> F = FunctionWrapper;
>> T = TimerWrapper(F);
>> clear T %# T's destructor not called
>> timerfind %# just to verify that no, the destructor was never called
Timer Object: timer-1
Timer Settings
ExecutionMode: fixedSpacing
Period: 1
BusyMode: drop
Running: off
Callbacks
TimerFcn: @(event,data)other_object.Fcn
ErrorFcn: ''
StartFcn: ''
StopFcn: ''
What's going on here? I know that timer
objects need to be manually deleted, but I thought that would be dealt with in the destructor for TimerWrapper
. Without using Amro's ugly but straightforward workaround to overload the clear
command, is there a way to clear T
from the workspace? Furthermore, nothing is referring to T
, so why does a reference to it exist? (The fact that the destructor is never called implies this fact.) Is this buried in the timer object itself?
If you type
t = TimerWrapper; f = functions(t.Timer.TimerFcn); f.workspace(2)
, you'll see that the workspace of the anonymous function used for the callback contains a reference to theTimerWrapper
object itself. So there's a kind of circular reference there which is not picked up byclear
.Given how you've set things up, you can remove
T
(and its underlying timer object) by calling the destructor explicitly and then callingclear
.The difference between
clear
anddelete
is kind of confusing (to me, anyway). As you've found,clear
doesn't explicitly call the destructor. It just removes the nameT
from the workspace. SoT
, and its underlying timer, still exist at that point. If they contained no references to things that still existed, MATLAB would then removeT
properly, including calling its destructor. As it is, since the timer contains a reference (in its callback) toT
, which still exists, the timer (and thusT
as well) is not deleted.You can find it (despite not having a name in the workspace) with
timerfindall
, and if you explicitly delete it yourself usingyou'll find that
T
is now properly gone.I'm not so sure that this is a bug, although I find it pretty confusing, and the distinction between
clear
anddelete
could probably be documented better.As to a workaround, I don't find it a big pain to explicitly call
delete
, although it's a bit more of a pain to clean things up if you accidentally callclear
. I would think the suggestion in message #5 from the thread you linked to, rather than message #4, would be more general and robust.I don't think you should overload
clear
in the way @Amro suggests in the separate thread you linked to: although this may work if you callclear T
explicitly, you may still get into trouble if you callclear all
, orclear variables
. (I haven't tried it just now, but I believe these syntaxes ofclear
don't even loop over things in the workspace and callclear
on each - instead they call theclear
method of the internal MATLAB workspace object, and that's going to get confusing fast).It seems as though this might be because the timer's callback is set to a non-default value. A proposed workaround (see message#4 of the linked thread) is to set the callback function only when the
start
method is called, then setting it to null when thestop
method is called.