좋은 블로그 포스트가 올라왔길래, 우리말로 정리해 보았습니다.
GObject 기반 객체 지향 프로그래밍에서 싱글턴 패턴을 사용하려면 대개 다음과 같은 함수를 추가합니다.
FooBar* foo_bar_get_default (void) { static FooBar *self = NULL; if (self == NULL) self = foo_bar_new (); return self; }
하지만 이렇게 구현할 경우 몇가지 단점이 있는데, 돌려받은 객체를 실수로 해제할 경우 문제를 일으킬 수 있고, 프로그램이 종료할때까지 객체가 소멸되지 않아 메모리 누수가 발생할 수 있습니다. 또한 사용자가 g_object_new(FOO_TYPE_BAR, NULL) 방식으로 객체를 생성하면 결국 새 객체가 만들어지기 때문에 싱글턴 객체로 동작하지 않습니다.
그래서, 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; }
이 방식을 이용하면 g_object_new()를 이용해 객체를 만들어도 항상 동일한 객체를 돌려줍니다. 더불어 객체의 참조카운터를 증가해서 돌려주기 때문에, 일반 객체처럼, 사용이 끝나면 g_object_unref()를 호출해 객체를 해제하면 됩니다. 물론 마지막 사용이 끝나는 시점에서는 자동으로 객체가 소멸되고 객체 포인터도 NULL값으로 초기화됩니다.(g_object_add_weak_pointer() 함수가 이 역할을 합니다)
물론 빈번한 객체 생성 / 소멸 호출을 막기 위해 프로그램 전반적으로 객체를 유지하든, 필요한 때만 생성해서 사용하도록 할 지 여부는 이 객체를 사용하는 프로그램이 선택할 수 있습니다. 따라서 라이브러리 코드를 작성할 경우 반드시 이 방식으로 싱글턴 객체를 제공하는게 좋습니다.
참고로, 위 구현은 멀티쓰레드가 동시에 접근하는 경우 안전하지 않습니다. 그러므로, 필요하다면, 뮤텍스나 g_once() 등을 이용해 객체 포인터를 보호해야 합니다.

This work, unless otherwise expressly stated, is licensed under a Creative Commons Attribution-Noncommercial-No Derivative Works 2.0 Korea License.