| |
Справочное описание GObject |
---|
Одной из замечательных особенностей GObject является родной механизм установки/получения свойств для объекта.
Когда объект инстанциирован, объектный обработчик class_init должен использоваться для регистрации свойств объекта
с помощью g_object_class_install_property
(реализована в gobject.c
).
Лучший способ понять как работают свойства объекта - посмотреть реальный пример их использования:
/************************************************/ /* Реализация */ /************************************************/ enum { MAMAN_BAR_CONSTRUCT_NAME = 1, MAMAN_BAR_PAPA_NUMBER, }; static void maman_bar_instance_init (GTypeInstance *instance, gpointer g_class) { MamanBar *self = (MamanBar *)instance; } static void maman_bar_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { MamanBar *self = (MamanBar *) object; switch (property_id) { case MAMAN_BAR_CONSTRUCT_NAME: { g_free (self->priv->name); self->priv->name = g_value_dup_string (value); g_print ("maman: %s\n",self->priv->name); } break; case MAMAN_BAR_PAPA_NUMBER: { self->priv->papa_number = g_value_get_uchar (value); g_print ("papa: %u\n",self->priv->papa_number); } break; default: /* We don't have any other property... */ G_OBJECT_WARN_INVALID_PROPERTY_ID(object,property_id,pspec); break; } } static void maman_bar_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { MamanBar *self = (MamanBar *) object; switch (property_id) { case MAMAN_BAR_CONSTRUCT_NAME: { g_value_set_string (value, self->priv->name); } break; case MAMAN_BAR_PAPA_NUMBER: { g_value_set_uchar (value, self->priv->papa_number); } break; default: /* We don't have any other property... */ G_OBJECT_WARN_INVALID_PROPERTY_ID(object,property_id,pspec); break; } } 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); GParamSpec *pspec; gobject_class->set_property = maman_bar_set_property; gobject_class->get_property = maman_bar_get_property; pspec = g_param_spec_string ("maman-name", "Maman construct prop", "Set maman's name", "no-name-set" /* default value */, G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE); g_object_class_install_property (gobject_class, MAMAN_BAR_CONSTRUCT_NAME, pspec); pspec = g_param_spec_uchar ("papa-number", "Number of current Papa", "Set/Get papa's number", 0 /* minimum value */, 10 /* maximum value */, 2 /* default value */, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, MAMAN_BAR_PAPA_NUMBER, pspec); } /************************************************/ /* Использование */ /************************************************/ GObject *bar; GValue val = {0,}; bar = g_object_new (MAMAN_TYPE_SUBBAR, NULL); g_value_init (&val, G_TYPE_CHAR); g_value_set_char (&val, 11); g_object_set_property (G_OBJECT (bar), "papa-number", &val);
Код клиента продемонстрированный выше прост, но большинство вещей происходит скоытно:
g_object_set_property
убеждается что свойство с таким названием было зарегистрировано обработчиком class_init в данной области. Если это так, она вызывает
object_set_property
которая обходит иерархию класса, от основания большинства производных типов, до
вершины базового типа в поиске класса который зарегистрировал это свойство. Затем она пытается конвертировать обеспеченную
пользователем GValue в GValue чей тип связан с этим свойством.
Если пользователь обеспечил signed char GValue, как показано здесь, и если свойство объекта было зарегистрировано как unsigned int,
g_value_transform
попытается преобразовать
введённый signed char в unsigned int. Конечно успех преобразования зависит от возможностей преобразующей функции.
На практике, почти всегда будет соответствующее преобразование [6]
и преобразование будет перенесено если необходимо.
После преобразования, GValue утверждается с помощью
g_param_value_validate
которая проверяет
чтобы пользовательские данные сохранённые в GValue
соответствовали характеристикам определённым GParamSpec
свойства. Здесь, GParamSpec мы обеспечили в функции
class_init имеющей функцию проверки допустимости значений содержащихся в GValue, которые придерживаются минимальных и максимальных
границ GParamSpec. В примере выше, GValue клиента не
придерживается этих ограничений (оно установлено в 11, тогда как максимум равен 10). Поэтому функция
g_object_set_property
вернёт ошибку.
Если пользовательское GValue установлено в допустимое значение,
g_object_set_property
продолжит процесс вызова объектного метода класса set_property. В примере, так как наша реализация Foo отменяла этот метод,
код перейдёт в foo_set_property
найдя
GParamSpec
с param_id [7]
который был сохранён с помощью
g_object_class_install_property
.
Как только свойство было установлено объектным методом класса set_property, код возвращается в
g_object_set_property
которая вызывает g_object_notify_queue_thaw
. Эта функция убеждается что сигнал "notify"
произошёл в экземпляре объекта с изменённым свойством в качестве параметра, если уведомления не были заморожены
g_object_freeze_notify
.
g_object_thaw_notify
может использоваться для нового уведомления изменения свойства через сигнал "notify". Важно помнить что даже если
свойства изменены в то время как уведомление изменения свойств заморожено, сигнал "notify" издаётся один раз для каждого
из изменённых свойств, как только уведомление изменения свойства разморожено: ни одно изменение свойства не теряется для
сигнала "notify". Сигнал может быть отсрочен с помощью механизма заморозки уведомления.
Похоже на утомительную задачу устанавливать GValues каждый раз когда необходимо изменить свойство.
На практике, очень редко придётся это делать. Функции
g_object_set_property
и g_object_get_property
предназначены для использования языковыми привязками. Для приложений есть более простой способ и он описан далее.
Интересно отметить что g_object_set
и
g_object_set_valist
(vararg версия) могут использоваться для установки множества свойств одновременно. Код клиента показанный выше можно переписать
как:
MamanBar *foo; foo = /* */; g_object_set (G_OBJECT (foo), "papa-number", 2, "maman-name", "test", NULL);
Это оберегает нас от управления GValues который мы должны были обработать используя
g_object_set_property
.
Код выше переключает одну эмиссию сигнала уведомления для каждого изменяемого свойства.
Конечно, версия _get также доступна:
g_object_get
и g_object_get_valist
(vararg версия) могут использоваться для получения множества свойств сразу.
Эти функции высокого уровня имеют один недостаток - они не обеспечивают возвращаемый результат. Нужно обращать внимание на типы аргументов и диапазон их использования. Известный источник ошибок например помещение gfloat вместо gdouble и таким образом смещение всех последующих параметров на четыре байта. Также отсутствие завершающего NULL может привести к неожиданному поведению.
Внимательные читатели теперь понимают как работают
g_object_new
,
g_object_newv
и
g_object_new_valist
:
они анализируют количество пользовательских переменных и вызывают
g_object_set
для параметров только после полного проектирования объекта.
Естественно, сигнал "notify" издаётся для каждого установленного свойства.
[6] Это может быть не то что вам нужно, но это позволяет вам решать полагаться ли на эти преобразования.
[7] Должно быть отмечено, что используемый здесь param_id уникален для идентификации каждого GParamSpec внутри FooClass так что переключение используемое в методах установка и получение фактически работает. Конечно, эти локальные уникальные целочисленные - просто оптимизация: возможно было бы использовать набор условий if (strcmp (a, b) == 0) {} else if (strcmp (a, b) == 0) {}.
Закладки на сайте Проследить за страницей |
Created 1996-2024 by Maxim Chirkov Добавить, Поддержать, Вебмастеру |