澄清已删除小部件的 GTK 内存管理

问题描述 投票:0回答:1

当我从容器中删除小部件时,我需要澄清 GTK 的内存管理。

/* Create a label widget with a floating reference. */
GtkWidget *lbl_text = gtk_label_new ("Text");

/* Create a grid container widget. */
GtkWidget *grid_text = gtk_grid_new ();

/* Add the label to the grid, which replaces the floating reference with a standard reference. */
gtk_grid_attach ( GTK_GRID(grid_text), lbl_text, 0, 0, 3, 1);

当我销毁网格

grid_text
时,GTK 将标签上的引用计数递减为零,并释放标签的内存。一切都好。

如果我从网格中删除标签,标签的引用计数会发生什么?

gtk_grid_remove (GTK_GRID(grid_text), lbl_text);

lbl_text
现在的引用计数是否为零并被销毁,或者它的浮动引用是否为1并且我需要使用
g_object_unref()
手动销毁它?

c gtk
1个回答
0
投票

我们可以看一下源码:

gtk_grid_remove
-->
gtk_widget_unparent
-->
g_object_unref

所以,快速的答案是清理完成。但是,有些代码有点复杂......


这是

gtk_grid_remove
代码:

static void
gtk_grid_remove (GtkContainer *container,
                 GtkWidget    *child)
{
  GtkGrid *grid = GTK_GRID (container);
  GtkGridPrivate *priv = grid->priv;
  GtkGridChild *grid_child;
  GList *list;

  for (list = priv->children; list; list = list->next)
    {
      grid_child = list->data;

      if (grid_child->widget == child)
        {
          gboolean was_visible = _gtk_widget_get_visible (child);

          gtk_widget_unparent (child);

          priv->children = g_list_remove (priv->children, grid_child);

          g_slice_free (GtkGridChild, grid_child);

          if (was_visible && _gtk_widget_get_visible (GTK_WIDGET (grid)))
            gtk_widget_queue_resize (GTK_WIDGET (grid));

          break;
        }
    }
}

AFAICT,唯一可以降低引用计数的调用是:gtk_widget_unparent


这是

gtk_widget_unparent

代码:

/** * gtk_widget_unparent: * @widget: a #GtkWidget * * This function is only for use in widget implementations. * Should be called by implementations of the remove method * on #GtkContainer, to dissociate a child from the container. **/ void gtk_widget_unparent (GtkWidget *widget) { GtkWidgetPrivate *priv; GObjectNotifyQueue *nqueue; GtkWidget *toplevel; GtkWidget *old_parent; g_return_if_fail (GTK_IS_WIDGET (widget)); priv = widget->priv; if (priv->parent == NULL) return; /* keep this function in sync with gtk_menu_detach() */ gtk_widget_push_verify_invariants (widget); g_object_freeze_notify (G_OBJECT (widget)); nqueue = g_object_notify_queue_freeze (G_OBJECT (widget), _gtk_widget_child_property_notify_context); toplevel = _gtk_widget_get_toplevel (widget); if (_gtk_widget_is_toplevel (toplevel)) _gtk_window_unset_focus_and_default (GTK_WINDOW (toplevel), widget); if (gtk_container_get_focus_child (GTK_CONTAINER (priv->parent)) == widget) gtk_container_set_focus_child (GTK_CONTAINER (priv->parent), NULL); gtk_widget_queue_draw_child (widget); /* Reset the width and height here, to force reallocation if we * get added back to a new parent. This won't work if our new * allocation is smaller than 1x1 and we actually want a size of 1x1... * (would 0x0 be OK here?) */ priv->allocation.width = 1; priv->allocation.height = 1; if (_gtk_widget_get_realized (widget)) { if (priv->in_reparent) gtk_widget_unmap (widget); else gtk_widget_unrealize (widget); } /* If we are unanchoring the child, we save around the toplevel * to emit hierarchy changed */ if (priv->parent->priv->anchored) g_object_ref (toplevel); else toplevel = NULL; /* Removing a widget from a container restores the child visible * flag to the default state, so it doesn't affect the child * in the next parent. */ priv->child_visible = TRUE; old_parent = priv->parent; priv->parent = NULL; /* parent may no longer expand if the removed * child was expand=TRUE and could therefore * be forcing it to. */ if (_gtk_widget_get_visible (widget) && (priv->need_compute_expand || priv->computed_hexpand || priv->computed_vexpand)) { gtk_widget_queue_compute_expand (old_parent); } /* Unset BACKDROP since we are no longer inside a toplevel window */ gtk_widget_unset_state_flags (widget, GTK_STATE_FLAG_BACKDROP); if (priv->context) gtk_style_context_set_parent (priv->context, NULL); gtk_css_node_set_parent (widget->priv->cssnode, NULL); _gtk_widget_update_parent_muxer (widget); g_signal_emit (widget, widget_signals[PARENT_SET], 0, old_parent); if (toplevel) { _gtk_widget_propagate_hierarchy_changed (widget, toplevel); g_object_unref (toplevel); } /* Now that the parent pointer is nullified and the hierarchy-changed * already passed, go ahead and unset the parent window, if we are unparenting * an embedded GtkWindow the window will become toplevel again and hierarchy-changed * will fire again for the new subhierarchy. */ gtk_widget_set_parent_window (widget, NULL); g_object_notify_by_pspec (G_OBJECT (widget), widget_props[PROP_PARENT]); g_object_thaw_notify (G_OBJECT (widget)); if (!priv->parent) g_object_notify_queue_clear (G_OBJECT (widget), nqueue); g_object_notify_queue_thaw (G_OBJECT (widget), nqueue); gtk_widget_pop_verify_invariants (widget); g_object_unref (widget); }
请注意,它所做的

最后事情是g_object_unref


AFAICT,

如果小部件是顶级的,该函数会执行内部g_object_ref

,然后在执行最后的
g_object_unref
之前执行
g_object_unref
。这些是平衡调用,因此可能没有变化。

因此,粗略的结论是该函数

确实清理了一切。

但是,这个函数[有点]复杂,调用小部件上的

many gtk_*

 函数,所以我无法判断最后一个调用是否只是为了补偿其中一个 
gtk_*
 调用增加了引用计数.

但是,这对我来说似乎很奇怪,所以我的[天真/简单]结论是对

g_object_unref

 的最终调用可以满足你的要求。

因此,在不检查所有拨打的各种电话的情况下,买者自负......


最好的方法可能是你做一些技巧来自己访问重新计数并查看之前和之后的值。这可能是 UB(免费后使用),但对于一次性测试来说还可以。

为了防止 UB,您可以将网格销毁调用包装在您自己/人工的

g_object_ref

g_object_unref
 对中。这将阻止对象被回收,直到进行 
your g_object_unref
 调用。

© www.soinside.com 2019 - 2024. All rights reserved.