A while back some people asked me to blog about how to make a GObject singleton. I think we are doing it right in Empathy’s code.
The most common way of doing a singleton is to add a function like that:
FooBar* foo_bar_get_default (void) { static FooBar *self = NULL; if (self == NULL) self = foo_bar_new (); return self; }
It means that you should never unref the return value, and the singleton is leaked for the entire live of the process (unless you play some atexit magic). It also means that g_object_new(FOO_TYPE_BAR, NULL) will return a new object and not a singleton, which is not friendly for bindings.
Here is how we are doing it most of the time in Empathy:
static GObject* constructor (GType type, guint n_construct_params, GObjectConstructParam *construct_params) { static GObject *self = NULL; if (self == NULL) { self = G_OBJECT_CLASS (foo_bar_parent_class)->constructor ( type, n_construct_params, construct_params); g_object_add_weak_pointer (self, (gpointer) &self); return self; } return g_object_ref (self); } static void foo_bar_class_init (FooBarClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructor = constructor; }
With that code, g_object_new() will return a singleton, and will always return a new ref. That means that you have to call g_object_unref() on the singleton once you don’t need it anymore, just like any normal object. If the last user of the singleton drops its ref, then the object is finalized (so there is no leak) and next time someone needs the singleton, a new one will be created (thanks to the weak pointer).
Of course to avoid object create/destroy ping-pong, you could want to keep a ref for the whole live of the process anyway, it really depend on your needs. But if you are writing a library, you cannot know in advance if the object will be needed for the whole live of the process, so it’s better to let the program using the library decide by managing the refcount itself.
Update: Thanks to Rony and Alexander for pointing me that this is not thread-safe. I think we have to use a mutex in that case. If someone has a better idea, please tell me 🙂