我在Gtk+
中编写了一些C
代码,它使用Cairo
和计时器执行一些动画。大多数情况下,当我点击关闭应用程序图标时,我在终端上收到以下消息:
Gtk-CRITICAL **:gtk_widget_queue_draw:断言`GTK_IS_WIDGET (小部件)'失败
现在我假设这是在讨价还价,因为在我关闭应用程序的那一刻,计时器被触发并且主窗口小部件被访问但是后来被破坏了。确定Gtk小部件是否仍然有效且可以引用的常用方法是什么?
违规代码在这里:
gboolean rotate_cb( void *degrees )
{
rotation += DegreesToRadians((*(int*)(degrees)));
// Tell our window that it should repaint itself (ie. emit an expose event)
/* need to only call gtk_widget_queue_draw() if window is still valid / exists */
gtk_widget_queue_draw(window);
return( TRUE );
}
我认为必须有某种方法来测试window
是否仍然有效且有效?
答案 0 :(得分:10)
你的问题非常微妙。这通常是因为GObject / GtkObject的所有权和破坏规则而发生的。让我提醒他们:
GObject
只是计数参考。当计数达到0时,它们被销毁。新创建的对象计数为1. GInitiallyUnowned
也被计数引用,并且当计数达到0时它们也被销毁。但是新创建的对象具有浮动计数。这意味着第一次递增计数时它实际上没有递增,但浮动计数是 sunk ,即转换为普通参考。 GtkObject
是 GInitiallyUnowned
个对象,因此它们具有神奇的浮动计数。
但你可能知道这一切......现在,我的问题:
谁拥有可见的主要GtkWindow的柜台?
这实际上很简单,GTK框架有一个列表,并保留每个可见GtkWindow
的引用。但是,另一个问题是:
什么时候GTK框架免费提供其GtkWindow的引用?
你还记得gtk_widget_destroy()
函数和destroy
信号吗?它们正是为了这个:当你想要删除一个调用GtkWindow
的顶层gtk_widget_destroy()
时,它会激活信号destroy
,它被GTK框架接收,删除了实际的窗口并释放了它们它对对象的引用。
这就是问题的原因:如果GTK框架保留了对GtkWindow
的唯一现有引用,则实际释放该对象。如果那时,你的计时器试图访问它,它将失败,因为窗口不再存在。
最后,(希望)来了解决方案:
g_object_ref()/g_object_ref_sink()
。还要将处理程序注册到窗口的destroy
信号。destroy
信号的处理程序中:在窗口上调用g_object_unref()
并停止计时器。当然,这种局部解决方案也应该有效,因为在不发送destroy
信号的情况下窗口不会被破坏:
destroy
信号。destroy
信号的处理程序中:停止计时器。但是当你实际保持对象的指针时,增加对象的ref计数器被认为是一种很好的做法。
答案 1 :(得分:3)
g_object_weak_ref()
对这些案例也很有用
在完成对象时,弱引用用于通知。
你可以连接一个弱引用回调(ex。widget_destroy_cb
),以便在它被销毁之前从小部件中断开rotate_cb
,因此一旦小部件被销毁就不应该调用rotate_cb
。
答案 2 :(得分:2)
没有办法,因为你查询对象的状态不再存在于内存中,即一些未定义的内存块。这种功能可能会失败,例如如果在同一个内存地址创建了另一个小部件。原则上,您可以使用GTK_IS_WIDGET()
宏,但无法保证正常工作,即使它最常见。
解决问题的正确方法是在rotate_cb()
被销毁时删除window
回调。有" destroy" GtkWidget
类中的信号。所以,你应该连接到" destroy"并在该处理程序中删除rotate_cb()
(或设置一些使rotate_cb()
无效的标志。)