binding: Add an explicit unbind()

Higher order languages with garbage collection can have issues releasing
a binding, as they do not control the last reference being dropped on
the binding, source, or target instances.

https://bugzilla.gnome.org/show_bug.cgi?id=698018
This commit is contained in:
Emmanuele Bassi 2013-04-14 21:04:41 +01:00
parent f61daa6ed2
commit a360b314aa
4 changed files with 95 additions and 29 deletions

View File

@ -935,6 +935,7 @@ g_binding_get_source_property
g_binding_get_target
g_binding_get_target_property
g_binding_get_flags
g_binding_release
<SUBSECTION>
g_object_bind_property
GBindingTransformFunc

View File

@ -96,6 +96,11 @@
* either one of the #GObject instances it refers to are finalized, or when
* the #GBinding instance loses its last reference.
*
* <note><para>Bindings for languages with garbage collection can use
* g_binding_unbind() to explicitly release a binding between the source
* and target properties, instead of relying on the last reference on the
* binding, source, and target instances to drop.</para></note>
*
* #GBinding is available since GObject 2.26
*/
@ -244,6 +249,8 @@ weak_unbind (gpointer user_data,
g_object_weak_unref (binding->source, weak_unbind, user_data);
remove_binding_qdata (binding->source, binding);
binding->source_notify = 0;
binding->source = NULL;
}
@ -257,6 +264,8 @@ weak_unbind (gpointer user_data,
g_object_weak_unref (binding->target, weak_unbind, user_data);
remove_binding_qdata (binding->target, binding);
binding->target_notify = 0;
binding->target = NULL;
}
@ -431,35 +440,7 @@ g_binding_finalize (GObject *gobject)
{
GBinding *binding = G_BINDING (gobject);
/* dispose of the transformation data */
if (binding->notify != NULL)
{
binding->notify (binding->transform_data);
binding->transform_data = NULL;
binding->notify = NULL;
}
/* we need this in case the source and target instance are still
* valid, and it was the GBinding that was unreferenced
*/
if (binding->source != NULL)
{
if (binding->source_notify != 0)
g_signal_handler_disconnect (binding->source, binding->source_notify);
g_object_weak_unref (binding->source, weak_unbind, binding);
remove_binding_qdata (binding->source, binding);
}
if (binding->target != NULL)
{
if (binding->target_notify != 0)
g_signal_handler_disconnect (binding->target, binding->target_notify);
g_object_weak_unref (binding->target, weak_unbind, binding);
remove_binding_qdata (binding->target, binding);
}
g_binding_unbind (binding);
G_OBJECT_CLASS (g_binding_parent_class)->finalize (gobject);
}
@ -771,6 +752,56 @@ g_binding_get_target_property (GBinding *binding)
return binding->target_property;
}
/**
* g_binding_unbind:
* @binding: a #GBinding
*
* Explicitly releases the binding between the source and the target
* property expressed by @binding.
*
* This function does not change the reference count of @binding.
*
* Since: 2.38
*/
void
g_binding_unbind (GBinding *binding)
{
g_return_if_fail (G_IS_BINDING (binding));
/* dispose of the transformation data */
if (binding->notify != NULL)
{
binding->notify (binding->transform_data);
binding->transform_data = NULL;
binding->notify = NULL;
}
if (binding->source != NULL)
{
if (binding->source_notify != 0)
g_signal_handler_disconnect (binding->source, binding->source_notify);
g_object_weak_unref (binding->source, weak_unbind, binding);
remove_binding_qdata (binding->source, binding);
binding->source_notify = 0;
binding->source = NULL;
}
if (binding->target != NULL)
{
if (binding->target_notify != 0)
g_signal_handler_disconnect (binding->target, binding->target_notify);
g_object_weak_unref (binding->target, weak_unbind, binding);
remove_binding_qdata (binding->target, binding);
binding->target_notify = 0;
binding->target = NULL;
}
}
/**
* g_object_bind_property_full:
* @source: (type GObject.Object): the source #GObject

View File

@ -115,6 +115,8 @@ GLIB_AVAILABLE_IN_ALL
const gchar * g_binding_get_source_property (GBinding *binding);
GLIB_AVAILABLE_IN_ALL
const gchar * g_binding_get_target_property (GBinding *binding);
GLIB_AVAILABLE_IN_2_38
void g_binding_unbind (GBinding *binding);
GLIB_AVAILABLE_IN_ALL
GBinding *g_object_bind_property (gpointer source,

View File

@ -570,6 +570,37 @@ binding_same_object (void)
g_object_unref (source);
}
static void
binding_unbind (void)
{
BindingSource *source = g_object_new (binding_source_get_type (), NULL);
BindingTarget *target = g_object_new (binding_target_get_type (), NULL);
GBinding *binding;
binding = g_object_bind_property (source, "foo",
target, "bar",
G_BINDING_DEFAULT);
g_object_add_weak_pointer (G_OBJECT (binding), (gpointer *) &binding);
g_object_set (source, "foo", 42, NULL);
g_assert_cmpint (source->foo, ==, target->bar);
g_object_set (target, "bar", 47, NULL);
g_assert_cmpint (source->foo, !=, target->bar);
g_binding_unbind (binding);
g_assert (binding != NULL);
g_object_set (source, "foo", 0, NULL);
g_assert_cmpint (source->foo, !=, target->bar);
g_object_unref (source);
g_object_unref (target);
g_object_unref (binding);
g_assert (binding == NULL);
}
int
main (int argc, char *argv[])
{
@ -586,6 +617,7 @@ main (int argc, char *argv[])
g_test_add_func ("/binding/sync-create", binding_sync_create);
g_test_add_func ("/binding/invert-boolean", binding_invert_boolean);
g_test_add_func ("/binding/same-object", binding_same_object);
g_test_add_func ("/binding/unbind", binding_unbind);
return g_test_run ();
}