The OpenNET Project / Index page

[ новости /+++ | форум | wiki | теги | ]

Каталог документации / Раздел "Программирование, языки" / Оглавление документа

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




Спонсоры:
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2021 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру