【问题标题】:Why isn't this object destroyed when it is cleared from the workspace?为什么这个对象从工作区中清除时没有被销毁?
【发布时间】:2023-03-30 18:00:02
【问题描述】:

我正在研究一个 MATLAB 类,该类存储一个使用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

当我清除我的类时,变量将从工作区中清除,但不会调用 delete 析构函数。为什么不呢?

我意识到,我的仪器在仪器控制应用程序中仍然处于活动状态。如果我从那里删除我的 Instruments,我的 delete 析构函数会被调用。

我认为这是 tcpip 类的一些奇怪行为。

【问题讨论】:

  • 你没有描述你的析构函数在什么意义上“停止工作”。但是调试帮助不太可能成为这里的主题问题,因此您可能不能只是改写为“为什么我的代码不能以这种方式工作?”。如果您可以将问题描述为“我如何编写以特定方式运行的handle-class 析构函数?”那么这里可能有一个可挽救的问题。
  • 那是我的错。如果我清除工作区,则不再调用析构函数。我将编辑问题。
  • 请阅读minimal reproducible example。代码 sn-ps 对找出你的代码有什么问题几乎没有用处。
  • 听起来基本上是我在 SO Find where handle is stored in scope 上的第一个问题的重复。这不是一个容易调试的问题。
  • 我看到一个最小的示例是必要的。在构建我发现的最小可重现示例时,问题来自 tcpip 对象的回调函数。我编辑了我最初的问题并用示例替换了 sn-p。

标签: matlab destructor handle matlab-class


【解决方案1】:

听起来很像您的Instrument Control App 类中有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'

我们可以看到类变量是活动的,因为这与作为函数输入的类没有区别,并且当该函数完成时,该变量的本地副本从作用域中清除。

如果我们真的想删除变量,那么您可以执行以下操作,显式运行析构方法:

var = basicClass;
var.name = '123';
otherReference.var = var;
delete(var);
otherReference.var.name

delete 行给我们:

123 being deleted

如果我们查看otherReference.var,我们会得到:

Invalid or deleted object.

这实际上会删除变量,您会看到otherReference 现在是指向无效句柄的指针。

【讨论】:

  • 这确实帮助我更好地理解问题所在,但并没有真正解决我的问题。我发布了一个 Awnser,它是在 Mathworks 支持员工的帮助下创建的。
【解决方案2】:

问题在于obj.TCPIP.BytesAvailableFcn = {@obj.TCP_Callback, obj}; 行。引用@obj.TCP_Callback 在 tcpip 对象中创建了一个类的实例,它是类本身的一个属性。

最简单的解决方案是清除析构函数内的obj.TCPIP.BytesAvailableFcn = '' 并手动调用析构函数。如果代码仅供其创建者使用,这是可以的。

为了保持面向对象的代码完整,包装类适用于回调函数。主文件使用 Wrapper Class 和 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

【讨论】:

    猜你喜欢
    • 2016-08-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-14
    • 1970-01-01
    • 2015-04-24
    • 1970-01-01
    • 2013-04-24
    相关资源
    最近更新 更多