当其中一个方法用作自己属性的回调时,如何调用对象的析构函数?

时间:2014-06-12 16:20:44

标签: matlab oop

我正在尝试编写一个包裹serial端口的类来读取传感器:

classdef sensor < handle
    properties
        Value
        s
    end

    methods
         function obj = sensor(port)
             obj.s = serial(port);
             obj.s.BytesAvailableFcn = @(o,e) obj.getData;

             fopen(obj.s);
         end
         function delete(obj)
             disp('called destructor');
             try
                 fclose(obj.s);
                 delete(obj.s);
             end
          end
          function getData(obj)
              obj.Value = fscanf(obj.s, '%d');
          end
     end
end

当我尝试清除工作区时,析构函数永远不会被调用:

>> foo = sensor('COM1');
>> clear foo % should disp() something!

根据我的previous experiences,仍然必须引用foo。事实证明它嵌入在串行端口foo.s

>> ports = instrfindall;
>> ports.BytesAvailableFcn
ans = 
    @(o,e)obj.getData

我清除了BytesAvailableFcn,即

>> ports.BytesAvailableFcn = '';

然后clear all,我显示了called destructor

如何打破此循环引用并让我的析构函数被调用?如果没有调用析构函数,则串口保持绑定。

2 个答案:

答案 0 :(得分:2)

问题并不在于你有一个循环引用; MATLAB理论上应该抓住那些。问题是循环引用是通过Java进行的,而MATLAB无法捕获它。

更详细地说 - 您的serial对象在内部包含一个Java对象,这是捕获与串行端口的连接的真实对象。设置回调时,MATLAB会设置底层Java对象的回调。如果回调是包含串行对象作为属性的对象的方法,那么您有一个循环引用通过底层Java对象。当你调用clear时,MATLAB会检查循环引用,如果它们全部只在MATLAB中,它会捕获它们,并调用析构函数 - 但它不会捕获它们,因为它通过Java。

一个解决方案是显式调用析构函数,也许这不是太麻烦。

另一种解决方法可能是简单地将回调作为类的方法 - 也许它可能只是一个常规函数,因为它似乎并不需要任何信息来自主对象本身,而不是对串行对象的引用。如果您希望将所有代码保存在单个文件中,也许您可​​以将其创建为匿名函数(虽然要小心,因为匿名函数将捕获它创建的工作空间,这取决于您创建它的位置可能包括对主对象的引用,在这种情况下,您将再次获得循环引用。

编辑:Rody是对的 - 我道歉,我读得太快了,并没有注意到getData实际上设置了Value属性而不仅仅是返回数据。 Rody在他的回答中的解决方案可能是另一种解决方法,但就像他说 - 哎呀。我只需手动拨打delete

答案 1 :(得分:1)

有趣的问题! :)

我找到了一个解决方法,但它不会很漂亮。

  • 您无法使用匿名功能。这将捕获本地工作空间,其中包含对obj的引用。你必须使用一个级别的间接(对Static方法,否则你会遇到同样的问题)。
  • 此静态方法可以安全地返回函数句柄。该函数句柄是一个函数,必须传递仪器对象。
  • 如果不传递对象的引用,设置Value属性当然是不可能的,因此,您必须在函数中创建一个全局状态,一个可变参数调用签名和一个{{{}的getter。 1}}属性。

我觉得我已经过度设计了这一点(毕竟是星期五),所以如果有人看到更简单的方法,请纠正我。无论如何,这就是我的意思:

Value

只需....呸。