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 the TimerWrapper
object itself. So there's a kind of circular reference there which is not picked up by clear
.
Given how you've set things up, you can remove T
(and its underlying timer object) by calling the destructor explicitly and then calling clear
.
T.delete
clear T
The difference between clear
and delete
is kind of confusing (to me, anyway). As you've found, clear
doesn't explicitly call the destructor. It just removes the name T
from the workspace. So T
, and its underlying timer, still exist at that point. If they contained no references to things that still existed, MATLAB would then remove T
properly, including calling its destructor. As it is, since the timer contains a reference (in its callback) to T
, which still exists, the timer (and thus T
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 using
tmrs = timerfindall;
delete(tmrs);
you'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
and delete
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 call clear
. 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 call clear T
explicitly, you may still get into trouble if you call clear all
, or clear variables
. (I haven't tried it just now, but I believe these syntaxes of clear
don't even loop over things in the workspace and call clear
on each - instead they call the clear
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 the stop
method is called.