Справочное описание GObject |
---|
В двух предыдущих разделах обсуждались детали Динамической системы типов Glib и её система управления сигналами. Библиотека GObject так же содержит реализацию основного базового типа называемого GObject.
GObject это базовый классифицируемый инстанциируемый тип. Он осуществляет:
Управление памятью с подсчётом ссылок
Создание/уничтожение интерфейсов
Основные свойства объекта с установкой/получением функциональных пар
Лёгкое использование сигналов
Все библиотеки GNOME которые используют систему типов GLib (такие как Gtk+ и GStreamer) наследуют GObject который важно изучить, чтобы понять детально как это работает.
Семейство функций g_object_new
могут использоваться для инстанциации любого GType который наследует основной тип GObject. Все эти функции проверяют класс
и экземпляр сструктуры на правильную инициализацию в системе типов glib и вызывают один за другим метод конструктора класса,
который используется для:
Распределение и очистка памяти с помощью
g_type_create_instance
,
Инициализацию экземпляра объекта с созданными свойствами.
Хотя можно устанавливать класс и члены объекта (кроме полей указывающих на родителей) в нули, некоторые считают хорошей практикой установку явных значений для них.
Объекты которые наследуют GObject позволяют переписывать этот метод создания класса: однако перед этим, они должны привязываться к их родительскому методу конструктора:
GObject* (*constructor) (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties);
Например ниже показано как MamanBar перезаписывает родительский конструктор:
#define MAMAN_TYPE_BAR (maman_bar_get_type ())
#define MAMAN_BAR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_TYPE_BAR, MamanBar))
#define MAMAN_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MAMAN_TYPE_BAR, MamanBarClass))
#define MAMAN_IS_BAR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_BAR))
#define MAMAN_IS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MAMAN_TYPE_BAR))
#define MAMAN_BAR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MAMAN_TYPE_BAR, MamanBarClass))
typedef struct _MamanBar MamanBar;
typedef struct _MamanBarClass MamanBarClass;
struct _MamanBar {
GObject parent;
/* instance members */
};
struct _MamanBarClass {
GObjectClass parent;
/* class members */
};
/* used by MAMAN_TYPE_BAR */
GType maman_bar_get_type (void);
static GObject *
maman_bar_constructor (GType type,
guint n_construct_properties,
GObjectConstructParam *construct_properties)
{
GObject *obj;
{
/* Invoke parent constructor. */
MamanBarClass *klass;
GObjectClass *parent_class;
klass = MAMAN_BAR_CLASS (g_type_class_peek (MAMAN_TYPE_BAR));
parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
obj = parent_class->constructor (type,
n_construct_properties,
construct_properties);
}
/* do stuff. */
return obj;
}
static void
maman_bar_instance_init (GTypeInstance *instance,
gpointer g_class)
{
MamanBar *self = (MamanBar *)instance;
/* do stuff */
}
static void
maman_bar_class_init (gpointer g_class,
gpointer g_class_data)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
MamanBarClass *klass = MAMAN_BAR_CLASS (g_class);
gobject_class->constructor = maman_bar_constructor;
}
GType maman_bar_get_type (void)
{
static GType type = 0;
if (type == 0) {
static const GTypeInfo info = {
sizeof (MamanBarClass),
NULL, /* base_init */
NULL, /* base_finalize */
maman_bar_class_init, /* class_init */
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (MamanBar),
0, /* n_preallocs */
maman_bar_instance_init /* instance_init */
};
type = g_type_register_static (G_TYPE_OBJECT,
"MamanBarType",
&info, 0);
}
return type;
}
Если пользователь инстанциирует объект MamanBar с помощью:
MamanBar *bar = g_object_new (MAMAN_TYPE_BAR, NULL);
Если это первая инстанциация такого объекта, функция maman_b_class_init
будет вызвана после любой функции maman_b_base_class_init
.
Это позволит убедиться в том, что структура класса этого нового объекта правильно инициализирована.
Здесь maman_bar_class_init
как ожидается отменит объектные методы класса
и установит собственные. В примере выше, метод конструктора единственный отменённый метод:
он устанавливается в maman_bar_constructor
.
Как только g_object_new
получила ссылку на инициализированную структуру класса, она вызывает свой метод конструктора для создания экземпляра нового объекта.
Затем она просто отменяет maman_bar_class_init
в maman_bar_constructor
, вызывая её позже, потому что была реализована правильно,
она связывается со своим родительским конструктором. Проблема заключается в поиске родительского конструктора.
Метод (используемый в исходном коде GTK+) состоит в том, чтобы сохранить оригинальный конструктор в статической переменной
в maman_bar_class_init
и зтем многократно использовать её из
maman_bar_constructor
. Это вполне возможно и очень просто, но это не очень хороший метод,
предпочтительней использовать функции
g_type_class_peek
и
g_type_class_peek_parent
.
Наконец, попорядку, g_object_constructor
вызывается последней в цепочке.
Эта функция распределяет инстанциированный объектный буфер через
g_type_create_instance
это значит в этой точке вызывается функция instance_init если она зарегистрирована.
После выполнения instance_init, объект полностью инициализирован и должен быть готов ответить на любой пользовательский запрос.
Когда g_type_create_instance
выполнена, g_object_constructor
устанавливает свойства конструкции
(то есть: свойства полученные в g_object_new
)
и возвращается в пользовательский конструктор которому позволяет выполнить подходящую инициализацию экземпляра...
Процессс описанный выше может показаться немного сложным (он действительно усложнён по моему..) но он может быть
резюмирован таблицей ниже, которая описывает
g_object_new
и порядок её вызова.
Таблица 4. g_object_new
Время вызова | Вызываемая функция | Параметры функции | Ремарка |
---|---|---|---|
Первый вызов g_object_new для целевого типа |
функция base_init целевого типа | В дереве иерархии классов из базового типа в целевой тип. base_init вызывается один раз для каждой сструктуры класса. | У меня нет идей как это может использоваться. Если вы знаете хороший работающий пример как используется base_init класса, пожалуйста, сообщите мне. |
Функция class_init целевого типа | В структуре класса целевого типа | Здесь вы должны убедиться что инициализировали или отменили методы класса (то есть, указатели функций связанные с каждым методом класса) и создать сигналы и свойства связанные с вашим объектом. | |
Интерфейсная функция base_init | В интерфейсной vtable | ||
интерфейсная функция interface_init | В интерфейсной vtable | ||
Каждый вызов g_object_new для целевого типа |
Метод конструктора класса целевого типа: GObjectClass->constructor | В экземпляре объекта | Если вам нужно завершить инициализацию объекта после того как все свойства конструкции установлены, отмените метод конструктора и проверьте цепочку объектного родительского класса перед выполнением вашей собственной инициализации. Если сомневаетесь, не переписывайте метод конструктора. |
типовая функция instance_init | В дереве иерархии классов из базового типа в целевой тип. instance_init обеспечивается для каждого типа вызывая для каждого экземпляра сструктуры. | Обеспечьте функцию instance_init для инициализации вашего объекта перед установкой конструкционных свойств. Это предпочтительный способ инициализации экземпляра GObject. Эта функция эквивалентна конструкторам C++. |
Читатели могут быть смущены некоторым обстоятельством в порядке вызова функций:
в то время как, технически, метод классового конструктора вызывается перед
GType функцией instance_init (так как
g_type_create_instance
которая вызывает instance_init вызывается g_object_constructor
которая находится на верхнем уровне
классового метода конструктора и к которому привязываются пользователи), пользовательский код который выполняется в обеспеченном
пользователем конструкторе будет всегда выполняться после
GType функции instance_init, так как конструктор обеспеченный пользователем
должен (вы были предкпреждены) привязываться перед
выполнением чего-либо полезного.