信号
信号
信号系统的基础概念是信号的发出。信号按类型引入并通过字符串标识。为父类型引入的信号在派生类型中也是可用的,因此基本上它们是继承的按类型设施。
处理程序
信号发出主要涉及以精确定义的方式调用一组回调。这类回调主要有两类,即每个对象的回调和用户提供的回调。(尽管信号可以处理任何可实例化的类型,但在此之后的上下文中,这些类型被称为'对象类型',因为这是大多数用户会遇到信号的情况。)每个对象的回调通常被称为“对象方法处理程序”或“默认(信号)处理程序”,而用户提供的回调通常只是称为“信号处理程序”。
对象方法处理程序是在信号创建时提供的(这通常发生在对象类创建结束时),而用户提供的处理程序通常是在某些对象实例上连接和断开连接。
处理程序必须与其在参数和返回值(通常是void)中定义的信号类型相匹配。所有处理程序都将类型实例的指针作为它们的第一个参数,以及一个gpointer user_data作为它们的最后一个参数,信号定义的参数在中间。user_data始终填充有在处理程序连接到信号时提供的用户数据。处理程序的文档将其类型说明为GCallback
,但这只是一个通用占位符类型——是GSignal APIs多态性的一个产物。
当连接到信号处理程序时,如果使用g_signal_connect_swapped()
而不是g_signal_connect()
,则其第一个('实例')和最后一个('用户数据')参数可以互换。这有时可以方便地避免定义包装函数作为信号处理程序,而是直接传递一个接受用户数据作为其第一个参数的函数。
发出
信号发出由五个阶段组成,除非提前停止
-
调用
G_SIGNAL_RUN_FIRST
信号的物体方法处理程序 -
调用正常用户提供的信号处理程序(其中未设置
after
标志) -
调用
G_SIGNAL_RUN_LAST
信号的物体方法处理程序 -
调用用户提供的信号处理程序(其中设置
after
标志) -
调用
G_SIGNAL_RUN_CLEANUP
信号的物体方法处理程序
调用用户提供的信号处理程序的顺序是它们连接的顺序。
所有处理程序都可以在信号发出时提前停止信号发出,并且在信号发出的过程中可以连接、断开连接、阻止单个处理程序或解除对处理程序的阻塞。
在信号发出阶段的2和4中跳过用户处理程序有一些标准。
首先,用户处理程序可以是阻塞的。在回调调用期间省略要求解除阻塞的处理程序,为了解除阻塞状态,处理程序必须被阻塞相同数量的次数。
其次,在发出一个G_SIGNAL_DETAILED
信号时,传递给g_signal_emit()
的附加detail
参数必须与当前正在调用的信号处理程序的detail参数相匹配。为信号处理程序指定无detail参数(在连接信号规范时省略detail部分)作为通配符,与传递给发出的任何detail参数相匹配。
当使用detail
参数时,通常用它来传递对象属性名(例如,在中使用),但对于detail字符串的格式没有明确规定,只需确保其非空即可。
信号处理器的内存管理
如果你要将处理器连接到信号,并使用GObject
实例作为信号处理器的用户数据,你应该记得将g_signal_connect()
的调用与g_signal_handler_disconnect()
或g_signal_handlers_disconnect_by_func()
的调用配对。虽然当发出信号的对象最终确定时,信号处理器会自动断开连接,但在信号处理器用户数据被销毁时并不会自动断开连接。如果此用户数据是GObject
实例,则在它最终确定后仍从信号处理器使用它是错误的。
管理此类用户数据有两种策略。第一种是在用户数据(对象)最终确定时断开信号处理器(使用g_signal_handler_disconnect()
或g_signal_handlers_disconnect_by_func()
);这必须手动实现。对于非线程程序,可以使用g_signal_connect_object()
来自动实现此功能。然而,目前在多线程程序中使用它是不安全的。
第二种策略是在信号因其他原因断开连接后,对用户数据保持强引用。可以使用g_signal_connect_data()
自动实现。
建议使用第一种方法,因为如果信号处理器永远不会因某些原因断开连接,那么第二种方法可能会导致用户数据的实际内存泄漏。