glib/docs/reference/gobject/floating-refs.md
Matthias Clasen e298f1a078 docs: Move floating refs documentation to Markdown
And add some new sections on strategies for avoiding designing APIs
around floating refs.

Helps: #3037
2023-10-11 14:01:29 +01:00

4.7 KiB
Raw Blame History

Title: Floating references

Floating references

Note: Floating references are a C convenience API and should not be used in modern GObject code. Language bindings in particular find the concept highly problematic, as floating references are not identifiable through annotations, and neither are deviations from the floating reference behavior, like types that inherit from [class@GObject.InitiallyUnowned] and still return a full reference from [id@g_object_new].

[class@GObject.InitiallyUnowned] is derived from [class@GObject.Object]. The only difference between the two is that the initial reference of a GInitiallyUnowned is flagged as a "floating" reference. This means that it is not specifically claimed to be "owned" by any code portion. The main motivation for providing floating references is C convenience. In particular, it allows code to be written as:

container = create_container ();
container_add_child (container, create_child());

If container_add_child() calls [method@GObject.Object.ref_sink] on the passed-in child, no reference of the newly created child is leaked. Without floating references, container_add_child() can only acquire a reference the new child, so to implement this code without reference leaks, it would have to be written as:

Child *child;
container = create_container ();
child = create_child ();
container_add_child (container, child);
g_object_unref (child);

The floating reference can be converted into an ordinary reference by calling g_object_ref_sink(). For already sunken objects (objects that don't have a floating reference anymore), g_object_ref_sink() is equivalent to [method@GObject.Object.ref] and returns a new reference.

Since floating references are useful almost exclusively for C convenience, language bindings that provide automated reference and memory ownership maintenance (such as smart pointers or garbage collection) should not expose floating references in their API. The best practice for handling types that have initially floating references is to immediately sink those references after g_object_new() returns, by checking if the GType inherits from GInitiallyUnowned. For instance:

GObject *res = g_object_new_with_properties (gtype,
                                             n_props,
                                             prop_names,
                                             prop_values);

// or: if (g_type_is_a (gtype, G_TYPE_INITIALLY_UNOWNED))
if (G_IS_INITIALLY_UNOWNED (res))
  g_object_ref_sink (res);

return res;

Some object implementations may need to save an object's floating state across certain code portions (an example is GtkMenu in GTK3), to achieve this, the following sequence can be used:

// save floating state
gboolean was_floating = g_object_is_floating (object);
g_object_ref_sink (object);
// protected code portion

...

// restore floating state
if (was_floating)
  g_object_force_floating (object);
else
  g_object_unref (object); // release previously acquired reference

Replacing floating references with annotations

You should avoid basing your object hierarchy on floating references, as they are hard to understand even in C, and introduce additional limitations when consuming a GObject-based API in other languages.

One way to express the ownership transfer between container and child instances is to use ownership transfer annotations in your documentation and introspection data. For instance, you can implement this pattern:

container_add_child (container, create_child ());

without leaking by having container_add_child() defined as:

/**
 * container_add_child:
 * @self: a container
 * @child: (transfer full): the child to add to the container
 *
 * ...
 */
void
container_add_child (Container *container,
                     Child *child)
{
  container->children = g_list_append (container->children, child);
}

The container does not explicitly acquire a reference on the child; instead, the ownership of the child is transferred to the container. The transfer annotation will be used by language bindings to ensure that there are no leaks; and documentation tools will explicitly note that the callee now owns the value passed by the caller.

Replacing floating references with weak references

Another option for replacing floating references is to use weak references in place of strong ones. A container can acquire a weak reference on the child instance by using [method@GObject.Object.weak_ref]. Once the child instance loses its last strong reference, the container holding a weak reference is notified, and it can either remove the child from its internal list, or turn a weak reference into a strong one.