Vala:通过处理程序 ID 从 class 析构函数断开信号处理程序失败 - 为什么?
Vala: Disconnecting signal handler via Handler ID from class destructor fails - Why?
我正在努力了解 Vala 中 class 间信号的良好做法是什么。不知何故,似乎绝对没有关于此主题的文档。在学习了如何 之后,问题仍然存在,当信号处理程序被销毁时,如何最好地断开连接。
让我们考虑一个简单的例子。我们有两个 classes,Foo
和 Bar
,其中 Bar
的实例发出信号 bar_signal
,由 [=14] 的实例处理=].但是这个信号连接很弱,让Foo
实例确实被删除了,为什么连接还完好:
class Bar : Object {
public signal void bar_signal();
}
class Foo : Object {
string tag;
ulong hid;
Bar bar;
public Foo( Bar bar, string tag ) {
this.tag = tag;
this.bar = bar;
weak Foo weak_this = this;
hid = bar.bar_signal.connect( weak_this.handle );
}
~Foo() {
stdout.printf( "foo finalized\n" );
}
private void handle() {
stdout.printf( "handler: %s\n", tag );
}
}
public static void main( string[] args ) {
Bar bar = new Bar();
{
Foo foo = new Foo( bar, "x" );
bar.bar_signal(); // writes "handler: x"
} // foo destroyed at end of block
bar.bar_signal(); // writes nothing
}
根据 ,在 Foo
实例被销毁后,处理程序引用仍作为悬挂指针存在于 bar_signal
中,因为我们还没有断开它。
我考虑过加一个
bar.bar_signal.disconnect( handle );
Foo
的析构函数,工作正常。
但是,我认为在某些情况下,析构函数无法访问 handle
,例如当处理程序是闭包时。
问题: 我们也可以使用相应 connect
调用的 return 值断开处理程序与信号的连接。这工作得很好,例如在 connect
调用之后立即完成。但是为什么它在析构函数中失败了?
~Foo() {
stdout.printf( "foo finalized\n" );
bar.disconnect( hid ); // <--!!
}
失败并显示以下提示:
GLib-GObject-WARNING **: instance '0x8c8820' has no handler with id '1'
更新
更新 1: 有趣的是,如果 bar.disconnect( hid )
调用是从 Foo
的 dispose
方法中完成的,那么它是有效的。根据 GObject
's memory management, dispose
is called right before the object is finalized (which I think corresponds to the destructor in Vala) and it's intention is to cut all references to other objects. Notably, as pointed out by @AlThomas, "signal handlers [...] are not automatically disconnected when the signal handler user data is destroyed." So why can we disconnect the signal handler from dispose
, but not when Foo
is finalized, which is what GObject's documentation ?
的文档
我想到的唯一解释是:与我之前被告知的相反 Vala/GLib do 自行断开信号处理程序在处理和完成对象之间。然而,这对我来说听起来不太可能,所以我真的很感激进一步的澄清。
更新 2: 事实证明,以上并没有错:)
一般来说,Vala 的信号是 observer pattern using GLib's GObject signals. So a good source of information is GLib's own documentation. For example Signals and How to create and use signals 的一个实现。后者表示:
The signal system in GType is pretty complex and flexible: it is possible for its users to connect at runtime any number of callbacks (implemented in any language for which a binding exists) to any signal and to stop the emission of any signal at any state of the signal emission process.
复杂的主题需要良好的文档,您针对 Vala 提出的问题有助于建立关于该主题的知识体系。
对于您的问题,GLib 文档中最相关的部分可能是 Memory management of signal handlers。本节建议:
While signal handlers are automatically disconnected when the object emitting the signal is finalised, they are not automatically disconnected when the signal handler user data is destroyed...There are two strategies for managing such user data. The first is to disconnect the signal handler (using g_signal_handler_disconnect()
or g_signal_handlers_disconnect_by_func()
) when the user data (object) is finalised; this has to be implemented manually. For non-threaded programs, g_signal_connect_object()
can be used to implement this automatically...The second is to hold a strong reference...The first approach is recommended...
在深入了解 Vala 的工作原理时,一个有用的选项是 --ccode
与 valac
的切换。如果您查看程序中的 C 代码,您会看到 foo_construct
函数包含对 g_signal_connect_object()
的调用。所以 Vala 使用文档中概述的第一种方法,但在三个函数调用中建议 Vala 通过调用 g_signal_connect_object()
使用自动方法。
无需手动断开连接,这就是您收到警告的原因。 GLib 已经断开了信号。 GLib的文档在这里可能有点混乱,因为部分手动过程中有一个调用可以自动断开连接。
GLib 关于 Object destruction 的文档建议 "The destruction process of your object is in two phases: dispose and finalize"。所以大概断开连接是在处理阶段完成的,但是你必须查看 GLib 的源代码来确认这一点。
我正在努力了解 Vala 中 class 间信号的良好做法是什么。不知何故,似乎绝对没有关于此主题的文档。在学习了如何
让我们考虑一个简单的例子。我们有两个 classes,Foo
和 Bar
,其中 Bar
的实例发出信号 bar_signal
,由 [=14] 的实例处理=].但是这个信号连接很弱,让Foo
实例确实被删除了,为什么连接还完好:
class Bar : Object {
public signal void bar_signal();
}
class Foo : Object {
string tag;
ulong hid;
Bar bar;
public Foo( Bar bar, string tag ) {
this.tag = tag;
this.bar = bar;
weak Foo weak_this = this;
hid = bar.bar_signal.connect( weak_this.handle );
}
~Foo() {
stdout.printf( "foo finalized\n" );
}
private void handle() {
stdout.printf( "handler: %s\n", tag );
}
}
public static void main( string[] args ) {
Bar bar = new Bar();
{
Foo foo = new Foo( bar, "x" );
bar.bar_signal(); // writes "handler: x"
} // foo destroyed at end of block
bar.bar_signal(); // writes nothing
}
根据 Foo
实例被销毁后,处理程序引用仍作为悬挂指针存在于 bar_signal
中,因为我们还没有断开它。
我考虑过加一个
bar.bar_signal.disconnect( handle );
Foo
的析构函数,工作正常。
但是,我认为在某些情况下,析构函数无法访问 handle
,例如当处理程序是闭包时。
问题: 我们也可以使用相应 connect
调用的 return 值断开处理程序与信号的连接。这工作得很好,例如在 connect
调用之后立即完成。但是为什么它在析构函数中失败了?
~Foo() {
stdout.printf( "foo finalized\n" );
bar.disconnect( hid ); // <--!!
}
失败并显示以下提示:
GLib-GObject-WARNING **: instance '0x8c8820' has no handler with id '1'
更新
更新 1: 有趣的是,如果 bar.disconnect( hid )
调用是从 Foo
的 dispose
方法中完成的,那么它是有效的。根据 GObject
's memory management, dispose
is called right before the object is finalized (which I think corresponds to the destructor in Vala) and it's intention is to cut all references to other objects. Notably, as pointed out by @AlThomas, "signal handlers [...] are not automatically disconnected when the signal handler user data is destroyed." So why can we disconnect the signal handler from dispose
, but not when Foo
is finalized, which is what GObject's documentation
我想到的唯一解释是:与我之前被告知的相反 Vala/GLib do 自行断开信号处理程序在处理和完成对象之间。然而,这对我来说听起来不太可能,所以我真的很感激进一步的澄清。
更新 2: 事实证明,以上并没有错:)
一般来说,Vala 的信号是 observer pattern using GLib's GObject signals. So a good source of information is GLib's own documentation. For example Signals and How to create and use signals 的一个实现。后者表示:
The signal system in GType is pretty complex and flexible: it is possible for its users to connect at runtime any number of callbacks (implemented in any language for which a binding exists) to any signal and to stop the emission of any signal at any state of the signal emission process.
复杂的主题需要良好的文档,您针对 Vala 提出的问题有助于建立关于该主题的知识体系。
对于您的问题,GLib 文档中最相关的部分可能是 Memory management of signal handlers。本节建议:
While signal handlers are automatically disconnected when the object emitting the signal is finalised, they are not automatically disconnected when the signal handler user data is destroyed...There are two strategies for managing such user data. The first is to disconnect the signal handler (using
g_signal_handler_disconnect()
org_signal_handlers_disconnect_by_func()
) when the user data (object) is finalised; this has to be implemented manually. For non-threaded programs,g_signal_connect_object()
can be used to implement this automatically...The second is to hold a strong reference...The first approach is recommended...
在深入了解 Vala 的工作原理时,一个有用的选项是 --ccode
与 valac
的切换。如果您查看程序中的 C 代码,您会看到 foo_construct
函数包含对 g_signal_connect_object()
的调用。所以 Vala 使用文档中概述的第一种方法,但在三个函数调用中建议 Vala 通过调用 g_signal_connect_object()
使用自动方法。
无需手动断开连接,这就是您收到警告的原因。 GLib 已经断开了信号。 GLib的文档在这里可能有点混乱,因为部分手动过程中有一个调用可以自动断开连接。
GLib 关于 Object destruction 的文档建议 "The destruction process of your object is in two phases: dispose and finalize"。所以大概断开连接是在处理阶段完成的,但是你必须查看 GLib 的源代码来确认这一点。