为什么这个对象在从工作区中清除时没有被销毁?
Why isn't this object destroyed when it is cleared from the workspace?
我正在研究一个 MATLAB class,它存储一个使用 tcpip
创建的接口对象,并包含一个供接口对象使用的回调函数,如下例所示:
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
当我清除 class 时,变量将从工作区中清除,但不会调用 delete
析构函数。为什么不呢?
我意识到,我的乐器在乐器控制应用程序中仍然处于活动状态。如果我从那里删除我的仪器,我的 delete
析构函数被调用。
我认为这是 tcpip 的一些奇怪行为-class。
这听起来很像您在 Instrument Control App
class 中有 wsg50
变量引用,在这种情况下,当您从工作区中清除该变量时,因为它仍在别处引用它不是 deleted
。
让我们看一个简单的例子:
classdef myClass < handle
properties
name = '';
end
methods
function delete ( obj )
fprintf ( '%s being deleted\n', obj.name );
end
end
end
右边让运行一些代码:
var = basicClass;
var.name = '123';
如果我们清除这个变量然后你可以看到 delete
被调用:
>> clear var
being deleted
如果我们重新 运行 这段代码并在别处引用:
var = basicClass;
var.name = '123';
otherReference.var = var;
查看两个变量,它们是相同的(正如预期的那样):
>> var
var =
myClass with properties:
name: '123'
>> otherReference.var
ans =
myClass with properties:
name: '123'
那么如果我们 clear
var
并查看其他参考文献会发生什么
clear var
otherReference.var.name
>> otherReference.var
ans =
myClass with properties:
name: '123'
我们可以看到 class 变量是活的,因为它应该是活的,因为这与 class 作为函数的输入并且当该函数完成本地副本时没有区别该变量已从范围中清除。
如果我们真的想删除变量,那么您可以执行以下操作,其中您显式 运行 析构函数方法:
var = basicClass;
var.name = '123';
otherReference.var = var;
delete(var);
otherReference.var.name
delete
行给我们:
123 being deleted
而如果我们查看 otherReference.var
,我们会得到:
Invalid or deleted object.
这实际上会删除变量,您会看到 otherReference
现在是指向无效句柄的指针。
问题出在 obj.TCPIP.BytesAvailableFcn = {@obj.TCP_Callback, obj};
行。引用 @obj.TCP_Callback
在 tcpip 对象中创建了 class 的一个实例,它是 class 本身的一个 属性。
最简单的解决方案是清除 obj.TCPIP.BytesAvailableFcn = ''
内部的析构函数并手动调用析构函数。如果代码仅由其创建者使用,这没关系。
为了保持面向对象代码的完整性,Wrapper-Class 用于回调函数。主文件使用 Wrapper Class 和 Listener 更新,如下所示。回调的设置替换为两者的组合。 Destructor 然后需要调用 Wrapper 的析构函数(它从回调中删除它自己的实例)和 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
另外,Wrapper 如下所示:
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
我正在研究一个 MATLAB class,它存储一个使用 tcpip
创建的接口对象,并包含一个供接口对象使用的回调函数,如下例所示:
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
当我清除 class 时,变量将从工作区中清除,但不会调用 delete
析构函数。为什么不呢?
我意识到,我的乐器在乐器控制应用程序中仍然处于活动状态。如果我从那里删除我的仪器,我的 delete
析构函数被调用。
我认为这是 tcpip 的一些奇怪行为-class。
这听起来很像您在 Instrument Control App
class 中有 wsg50
变量引用,在这种情况下,当您从工作区中清除该变量时,因为它仍在别处引用它不是 deleted
。
让我们看一个简单的例子:
classdef myClass < handle
properties
name = '';
end
methods
function delete ( obj )
fprintf ( '%s being deleted\n', obj.name );
end
end
end
右边让运行一些代码:
var = basicClass;
var.name = '123';
如果我们清除这个变量然后你可以看到 delete
被调用:
>> clear var
being deleted
如果我们重新 运行 这段代码并在别处引用:
var = basicClass;
var.name = '123';
otherReference.var = var;
查看两个变量,它们是相同的(正如预期的那样):
>> var
var =
myClass with properties:
name: '123'
>> otherReference.var
ans =
myClass with properties:
name: '123'
那么如果我们 clear
var
并查看其他参考文献会发生什么
clear var
otherReference.var.name
>> otherReference.var
ans =
myClass with properties:
name: '123'
我们可以看到 class 变量是活的,因为它应该是活的,因为这与 class 作为函数的输入并且当该函数完成本地副本时没有区别该变量已从范围中清除。
如果我们真的想删除变量,那么您可以执行以下操作,其中您显式 运行 析构函数方法:
var = basicClass;
var.name = '123';
otherReference.var = var;
delete(var);
otherReference.var.name
delete
行给我们:
123 being deleted
而如果我们查看 otherReference.var
,我们会得到:
Invalid or deleted object.
这实际上会删除变量,您会看到 otherReference
现在是指向无效句柄的指针。
问题出在 obj.TCPIP.BytesAvailableFcn = {@obj.TCP_Callback, obj};
行。引用 @obj.TCP_Callback
在 tcpip 对象中创建了 class 的一个实例,它是 class 本身的一个 属性。
最简单的解决方案是清除 obj.TCPIP.BytesAvailableFcn = ''
内部的析构函数并手动调用析构函数。如果代码仅由其创建者使用,这没关系。
为了保持面向对象代码的完整性,Wrapper-Class 用于回调函数。主文件使用 Wrapper Class 和 Listener 更新,如下所示。回调的设置替换为两者的组合。 Destructor 然后需要调用 Wrapper 的析构函数(它从回调中删除它自己的实例)和 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
另外,Wrapper 如下所示:
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