docs: Move floating refs documentation to Markdown

And add some new sections on strategies for avoiding designing APIs
around floating refs.

Helps: #3037
This commit is contained in:
Matthias Clasen 2023-10-09 23:17:19 +01:00 committed by Philip Withnall
parent 58019515d6
commit e298f1a078
4 changed files with 130 additions and 80 deletions

View File

@ -0,0 +1,128 @@
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:
```c
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:
```c
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:
```c
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:
```c
// 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:
```c
container_add_child (container, create_child ());
```
without leaking by having `container_add_child()` defined as:
```c
/**
* 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.

View File

@ -42,6 +42,7 @@ show_class_hierarchy = true
urlmap_file = "urlmap.js"
# The same order will be used when generating the index
content_files = [
"floating-refs.md",
"boxed.md",
"enum-types.md",
]

View File

@ -73,6 +73,7 @@ endif
expand_content_files = [
'boxed.md',
'enum-types.md',
'floating-refs.md',
]
gobject_gir = meson.current_source_dir() / 'GObject-2.0.gir'

View File

@ -54,86 +54,6 @@
* GObjects and their methods, see the [GType conventions][gtype-conventions].
* For the high-level concepts behind GObject, read [Instantiatable classed types:
* Objects][gtype-instantiatable-classed].
*
* ## Floating references # {#floating-ref}
*
* **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 #GInitiallyUnowned and still return
* a full reference from g_object_new().
*
* GInitiallyUnowned is derived from GObject. 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:
*
* |[<!-- language="C" -->
* container = create_container ();
* container_add_child (container, create_child());
* ]|
*
* If container_add_child() calls g_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 g_object_ref() the new child,
* so to implement this code without reference leaks, it would have to be
* written as:
*
* |[<!-- language="C" -->
* 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 g_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:
*
* |[<!-- language="C" -->
* 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 objects floating state
* across certain code portions (an example is #GtkMenu), to achieve this,
* the following sequence can be used:
*
* |[<!-- language="C" -->
* // 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
* ]|
*/
/* --- macros --- */