MATLAB - objects not clearing when timers are invo

2019-06-03 05:27发布

问题:

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?

回答1:

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).



回答2:

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.



标签: oop matlab timer