【问题标题】:How to determine if GTK widget has been destroyed如何确定 GTK 小部件是否已被销毁
【发布时间】:2012-06-16 07:06:32
【问题描述】:

我有一些用C 编写的Gtk+ 代码,它使用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 是否仍然有效和有效?

【问题讨论】:

    标签: c gtk widget


    【解决方案1】:

    你的问题很微妙。这通常是因为 GObject/GtkObject 的所有权和销毁规则。让我提醒他们:

    • GObjects 只是引用计数。它们在计数达到 0 时被销毁。新创建的对象的计数为 1。
    • GInitiallyUnowneds 也是计数引用的,当计数达到 0 时它们也会被销毁。但是新创建的对象有一个 浮动计数。这意味着第一次增加计数时实际上并没有增加,而是浮动计数沉没,即转换为普通引用。

    GtkObjects 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-counter 被认为是一种好习惯。

    【讨论】:

    • 感谢罗德里戈非常详细的回复。我学到了很多,也很感激。
    • 一个很有教育意义的答案,+1!
    【解决方案2】:

    没有办法,因为你会查询不再在内存中的对象的状态,即一些未定义的内存块。这样的功能可能会失败,例如如果在同一内存地址创建了另一个小部件。原则上,您可以只使用 GTK_IS_WIDGET() 宏,但不能保证正常工作,即使大多数情况下会正常工作。

    解决问题的正确方法是在 window 被销毁时删除 rotate_cb() 回调。 GtkWidget 类中有“销毁”信号。因此,您应该连接到“destroy”并在该处理程序中删除 rotate_cb()(或设置一些标志,使 rotate_cb() 什么都不做)。

    【讨论】:

      【解决方案3】:

      g_object_weak_ref() 也适用于这些情况

      弱引用用于对象完成时的通知。

      您可以连接一个弱引用回调(例如widget_destroy_cb)以在它被销毁之前断开rotate_cb 与小部件的连接,因此一旦小部件被销毁,就不应调用rotate_cb

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-07-05
        • 1970-01-01
        • 2012-10-21
        • 2010-12-28
        • 2011-11-25
        • 2015-10-23
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多