当我从容器中删除小部件时,我需要澄清 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()
手动销毁它?
我们可以看一下源码:
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
如果小部件是顶级的,该函数会执行内部g_object_ref
,然后在执行最后的
g_object_unref
之前执行
g_object_unref
。这些是平衡调用,因此可能没有变化。因此,粗略的结论是该函数
确实清理了一切。
但是,这个函数[有点]复杂,调用小部件上的many gtk_*
函数,所以我无法判断最后一个调用是否只是为了补偿其中一个
gtk_*
调用增加了引用计数.但是,这对我来说似乎很奇怪,所以我的[天真/简单]结论是对
g_object_unref
的最终调用可以满足你的要求。因此,在不检查所有拨打的各种电话的情况下,买者自负......
为了防止 UB,您可以将网格销毁调用包装在您自己/人工的
g_object_ref
和
g_object_unref
对中。这将阻止对象被回收,直到进行your
g_object_unref
调用。