Reputation: 1008
I am working on a MATLAB class which stores an interface object created with tcpip
and includes a callback function for the interface object to use, as per the following example:
classdef wsg50_mini2 < handle
properties
TCPIP
end
%PUBLIC METHODS
methods
%CONSTRUCTOR
function obj = wsg50_mini2(varargin)
fprintf('################# I am created #################\n')
obj.TCPIP = tcpip('localhost',1000);
obj.TCPIP.OutputBufferSize = 3000;
obj.TCPIP.InputBufferSize = 3000;
obj.TCPIP.ByteOrder = 'littleEndian';
obj.TCPIP.Timeout = 1;
%Setting up Callbackfunction
obj.TCPIP.BytesAvailableFcnMode = 'byte';
obj.TCPIP.BytesAvailableFcnCount = 1;
obj.TCPIP.BytesAvailableFcn = {@obj.TCP_Callback, obj};
end
end
%PRIVATE METHODS
methods (Access = private)
%DESTRUCTOR
function delete(obj)
fprintf('################# Hey I am called! #################\n')
instrreset
end
end
%STATIC METHODS
methods (Static)
%TCP Callback
%This function will be called if one Byte is available at the TCPIP
%buffer.
function TCP_Callback(tcpsocket,event,obj)
fprintf('Loading 1 Byte Data From Buffer.\n')
end
end
end
When I clear my class the variable will be cleaned from the workspace, but the delete
destructor function is not called. Why not?
I realized, that my Instruments are still active in the Instrument Control app. If I delete my Instruments from there, my delete
destructor is be called.
I think it is some strange behaviour of the tcpip-class.
Upvotes: 0
Views: 245
Reputation: 1008
The problem is the line obj.TCPIP.BytesAvailableFcn = {@obj.TCP_Callback, obj};
. The reference @obj.TCP_Callback
creates a instance of the class inside the tcpip object, which is a property of the class itself.
the most easiest solution is to clear obj.TCPIP.BytesAvailableFcn = ''
inside the destructor and call the destructor manualy. This is okayish if the code is only used by its creator.
To keep the object-oriented code intact, a Wrapper-Class works for the callback-function. The main file is updated with the Wrapper Class and a Listener as shown below. The Setup of the Callback is replaced with a combination of both of them. The Destructor needs then to call the destructors of the Wrapper (Which removes the instance of itself from the Callback) and of the Listener:
classdef wsg50_mini2 < handle
properties
TCPIP
TCPIPWrapper
LH
end
%PUBLIC METHODS
methods
%CONSTRUCTOR
function obj = wsg50_mini2(varargin)
fprintf('################# I am created #################\n')
% Create TCPIP Object
obj.TCPIP = tcpip('localhost',1000);
% Create TCPIP Wrapper
obj.TCPIPWrapper = TCPIPWrapper(obj.TCPIP)
% Add Listener
obj.LH = listener(obj.TCPIPWrapper,'BytesAvailableFcn',@obj.onBytes);
obj.TCPIP.OutputBufferSize = 3000;
obj.TCPIP.InputBufferSize = 3000;
obj.TCPIP.ByteOrder = 'littleEndian';
obj.TCPIP.Timeout = 1;
end
end
%PRIVATE METHODS
methods (Access = private)
%DESTRUCTOR
function delete(obj)
fprintf('################# Hey I am called! #################\n')
% Destroy Listener
delete(obj.LH);
% destroy Wrapper
delete(obj.TCPIPWrapper);
instrreset
end
end
%STATIC METHODS
methods (Private)
function onBytes(obj,~,~)
fprintf('Loading 1 Byte Data From Buffer.\n')
end
end
end
Additional the Wrapper looks as following:
classdef TCPIPWrapper < handle
properties(Access=private)
tcpip
end
events
BytesAvailableFcn
end
methods
% tcpipCallbackWrapper Constructor
function obj = tcpipCallbackWrapper(tcpip)
disp 'CONSTRUCTED tcpipCallbackWrapper'
% Keep a reference to the tcpip object
obj.tcpip = tcpip;
% Adding this listener will result in the destructor not being
% called automatically anymore; but luckily we have myClass
% which will explicitly call it for us
obj.tcpip.BytesAvailableFcnMode = 'byte';
obj.tcpip.BytesAvailableFcnCount = 1;
obj.tcpip.BytesAvailableFcn = @obj.bytesAvailableFcn;
end
% tcpipCallbackWrapper Destructor
function delete(obj)
disp 'DESTRUCTED tcpipCallbackWrapper'
% Overwrite the callback with a new empty one, removing the
% reference from the callback to our class instance, allowing
% the instance to truly be destroyed
obj.tcpip.BytesAvailableFcn = '';
end
end
methods (Access=private)
% Callback for BytesAvailableFcn
function bytesAvailableFcn(obj,~,~)
notify(obj, 'BytesAvailableFcn');
end
end
end
Upvotes: 0
Reputation: 5672
It sounds very much like you have the wsg50
variable reference in your Instrument Control App
class, in that case when you clear the variable from the workspace because it is still referenced elsewhere it is not deleted
.
Lets look at a simple example:
classdef myClass < handle
properties
name = '';
end
methods
function delete ( obj )
fprintf ( '%s being deleted\n', obj.name );
end
end
end
Right lets run some code:
var = basicClass;
var.name = '123';
If we clear this variable then you can see the delete
is called:
>> clear var
being deleted
If we re run this code and make a reference elsewhere:
var = basicClass;
var.name = '123';
otherReference.var = var;
Looking at both variables they are the same (as expected):
>> var
var =
myClass with properties:
name: '123'
>> otherReference.var
ans =
myClass with properties:
name: '123'
So what happens if we clear
var
and look at the other reference
clear var
otherReference.var.name
>> otherReference.var
ans =
myClass with properties:
name: '123'
We can see that the class variable is alive, as it should be as this is no difference than a class being an input to a function and when that function finishes the local copy of that variable is cleared from scope.
If we really want to delete the variable then you can do the following, where you explicitly run the destructor method:
var = basicClass;
var.name = '123';
otherReference.var = var;
delete(var);
otherReference.var.name
The delete
line give us:
123 being deleted
While if we look at the otherReference.var
we get:
Invalid or deleted object.
This will actually delete the variable and you will see that the otherReference
is now a pointer to an invalid handle.
Upvotes: 2