当对象的方法之一用作其自身属性的回调时,如何调用对象的析构函数?
Posted
技术标签:
【中文标题】当对象的方法之一用作其自身属性的回调时,如何调用对象的析构函数?【英文标题】:How do I call an object's destructor when one of its methods is used as a callback for its own property? 【发布时间】:2014-06-12 16:20:43 【问题描述】:我正在尝试编写一个环绕 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!
根据我的previousexperiences,肯定还有对foo
的引用。原来这是嵌入在串口foo.s
中的:
>> ports = instrfindall;
>> ports.BytesAvailableFcn
ans =
@(o,e)obj.getData
一旦我清除了BytesAvailableFcn
,即
>> ports.BytesAvailableFcn = '';
然后clear all
,我会显示我的called destructor
。
我怎样才能打破这个循环引用并让我的析构函数被调用?如果没有调用析构函数,则串行端口保持绑定。
【问题讨论】:
显而易见的解决方案是显式调用delete
方法,但我希望有更好的方法来做到这一点。
【参考方案1】:
问题不在于你有一个循环引用; MATLAB 理论上应该能赶上这些。问题是循环引用是通过 Java 传递的,而 MATLAB 无法捕获它。
更详细 - 您的 serial
对象在内部包含一个 Java 对象,它是捕获与串行端口的连接的真实对象。当您设置其回调时,MATLAB 会设置底层 Java 对象的回调。如果回调是针对包含串行对象作为属性的对象的方法,那么您有一个通过底层 Java 对象的循环引用。当您调用 clear
时,MATLAB 会检查循环引用,如果它们都在 MATLAB 中,它会捕获它们并调用析构函数 - 但它不会在通过 Java 时捕获它们。
一种解决方案是显式调用析构函数,也许这并不太麻烦。
另一种解决方法可能是简单地 不 将回调作为类的方法 - 也许它可能只是一个常规函数,因为它似乎并不需要任何信息来自主对象本身,而不是对串行对象的引用。如果您希望将所有代码保存在一个文件中,也许您可以将其创建为匿名函数(尽管要小心,因为匿名函数将捕获它创建的工作区,这取决于您创建它的位置可能包括对主对象的引用,在这种情况下,您将再次获得循环引用。
编辑:Rody 说得对 - 抱歉,我读得太快了,没有注意到 getData
实际上设置了 Value
属性,而不仅仅是返回数据。罗迪在他的回答中的解决方案可能是另一种解决方法,但就像他说的那样 - 呸。我只是手动拨打delete
。
【讨论】:
我看不到如何在不引用对象的情况下创建一个函数(不依赖于某些全局状态),如果您希望它设置Value
属性...调用@ 987654327@ 手动会有我的偏好。【参考方案2】:
有趣的问题! :)
我找到了一种解决方法,但它不会很漂亮。
您不能使用匿名函数。这将捕获本地工作空间,其中将包含对obj
的引用。您必须使用一级间接(到 Static
方法,否则,您会遇到同样的问题)。
此静态方法可以安全地返回函数句柄。这个函数句柄是一个函数,它必须被传递给仪器对象。
如果不传递对对象的引用,当然不可能设置Value
属性,因此,您必须在函数中创建一个全局状态、一个可变调用签名和一个Value
属性的getter .
我觉得我对此进行了过度设计(毕竟是星期五),所以如果有人看到更简单的方法,请纠正我。无论如何,这就是我的意思:
classdef sensor < handle
properties
s
end
properties (Dependent)
Value
end
methods
function obj = sensor(port)
obj.s = serial(port);
% You cannot use function handle without implicitly passing obj into
% it. Instead, get a function handle from another function, one that
% does not have this problem:
obj.s.BytesAvailableFcn = sensor.getGetData(obj.s);
fopen(obj.s);
end
function delete(obj)
disp('called destructor');
try %#ok<TRYNC>
fclose(obj.s);
delete(obj.s);
end
end
% Use a getter for Value, so that whenever you query the Value property,
% you get the most recently read sensor data
function V = get.Value(obj) %#ok<MANU>
V = getData();
end
end
% Use indirection to a Static method, to avoid referencing obj implicitly
methods (Access = private, Static)
function f = getGetData(S)
f = @(o,e)getData(S);
end
end
end
% The actual sensor reader
function data = getData(S)
persistent port
if nargin == 1
port = S; return; end
try
data = fscanf(port, '%d');
catch ME
% ...
end
end
只是....糟糕。
【讨论】:
是的。干得好! 其实这带来了一个好处。如果我们不使用持久端口,而是使用instrfind('Type', 'serial', 'Port', nameOfPort)
,并传递端口名称(COM1 等)会怎样?然后,您将在类构造函数中实例化一个“匿名”serial
对象。我必须考虑一下。
这样您甚至不必将s
作为类的属性。我想知道这是否会有所帮助...
我猜持久化会有更好的性能。如果您经常阅读Value
,您不想每次都通过 all 端口进行搜索...OOP 明智地让类管理串行端口是有意义的,在换句话说,将其作为财产。但是如果 MATLAB/Java 接口有问题,而 this 是解决方法,那么也许你的想法更好:)以上是关于当对象的方法之一用作其自身属性的回调时,如何调用对象的析构函数?的主要内容,如果未能解决你的问题,请参考以下文章