Основной класс 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, так как конструктор обеспеченный пользователем должен (вы были предкпреждены) привязываться перед выполнением чего-либо полезного.