gobject->qdata的作用很大。
可以在&gobject->data上面绑定任何数据
ID | quark name | 说明 | |
1 | quark_closure_array | ||
2 | quark_weak_refs | ||
3 | quark_toggle_refs | ||
4 | property_notify_context.quark_notify_queue | 实现gobject property change的Notify功能 | |
5 | 用户自定义的各种其他quark data |
与属性相关的几个定义:
static void g_object_do_class_init (GObjectClass *class) { pspec_pool = g_param_spec_pool_new (TRUE); property_notify_context.quark_notify_queue = g_quark_from_static_string ("GObject-notify-queue"); property_notify_context.dispatcher = g_object_notify_dispatcher; class->set_property = g_object_do_set_property; class->get_property = g_object_do_get_property; class->dispatch_properties_changed = g_object_dispatch_properties_changed; class->notify = NULL; gobject_signals[NOTIFY] = g_signal_new (g_intern_static_string ("notify"), G_TYPE_FROM_CLASS (class), G_SIGNAL_RUN_FIRST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED | G_SIGNAL_NO_HOOKS | G_SIGNAL_ACTION, G_STRUCT_OFFSET (GObjectClass, notify), NULL, NULL, g_cclosure_marshal_VOID__PARAM, G_TYPE_NONE, 1, G_TYPE_PARAM); g_type_add_interface_check (NULL, object_interface_check_properties); }
//这两个基本上没有东西,需要被子类override:
class->set_property = g_object_do_set_property;
class->get_property = g_object_do_get_property;
我们从GObject 内部property机制/ 用户(使用GObject Property) 的2个角度看:
用户(使用GObject Property) |
1. install properties g_object_class_install_property //用的多 g_object_class_install_properties
2. 提供override GObjectClass的set/get property 类成员接口 如: gobject_class->set_property = gst_base_audio_sink_set_property; gobject_class->get_property = gst_base_audio_sink_get_property; 3. 调用GObject提供的系列get/set, 修改/获取property, 并且发信号“Notify"
? user 关心这个"notify"信号吗? 仅仅是get/set property的value?
如果user: g_signal_connect (obj, "notify", G_CALLBACK (on_notify), &data); 下面的APIs会激发notifiy queue去发射signal " notifiy": g_object_set g_object_notify_by_pspec g_object_notify 然后去执行用户的callback
|
GObject 内部property机制 |
1. 提供一个全局的pspec pool, 保存用户install的properties 2. 一个quark:quark_notify_queue, 用来标识Notify queue; 1)这个notify queue是保存pspec链表 nqueue->pspecs = g_slist_prepend (nqueue->pspecs, pspec);
2)notify queue作为qdata绑定给gobject上面 g_datalist_id_set_data_full (&object->qdata, context->quark_notify_queue, nqueue, g_object_notify_queue_free);
3. 一个dispatcher, 作用就是发射signal: "notify" 4. 与这个"notify"信号相关 1)定义一个信号GObject::notify 并绑定default handler: G_STRUCT_OFFSET (GObjectClass, notify), class->notify = NULL; 2)g_object_notify_queue_thaw context->dispatcher (object, n_pspecs, pspecs); property_notify_context.dispatcher = g_object_notify_dispatcher; static void g_object_notify_dispatcher (GObject *object, guint n_pspecs, GParamSpec **pspecs) { G_OBJECT_GET_CLASS (object)->dispatch_properties_changed (object, n_pspecs, pspecs); }
static void g_object_dispatch_properties_changed (GObject *object, guint n_pspecs, GParamSpec **pspecs) { guint i; for (i = 0; i < n_pspecs; i++) g_signal_emit (object, gobject_signals[NOTIFY], g_quark_from_string (pspecs[i]->name), pspecs[i]); } 如果用户user, 不用g_signal_connect(), 这样user level的c handler也是NULL new signal时,default handler = NULL 难道没有人care 这个"notify" signal吗?那还要这个spec notifcation机制干什么?
5. 提供给上层user使用的几个get/set property的API: void g_object_set (gpointer object, const gchar *first_property_name, ...) G_GNUC_NULL_TERMINATED; void g_object_get (gpointer object, const gchar *first_property_name, ...) G_GNUC_NULL_TERMINATED; void g_object_set_valist (GObject *object, const gchar *first_property_name, va_list var_args); void g_object_get_valist (GObject *object, const gchar *first_property_name, va_list var_args); void g_object_set_property (GObject *object, const gchar *property_name, const GValue *value); void g_object_get_property (GObject *object, const gchar *property_name, GValue *value); 上面的6个APIs, 以及g_object_new(), g_object_newv, g_object_new_vlist 都会触发g_object_notify_queue_thaw(),从而对所有的属性都发"notify" 信号? |
gobject property的使用,参考一下:
glib/gobject/tests/properties.c:
#include <stdlib.h> #include <gstdio.h> #include <glib-object.h> typedef struct _TestObject { GObject parent_instance; gint foo; gboolean bar; gchar *baz; } TestObject; typedef struct _TestObjectClass { GObjectClass parent_class; } TestObjectClass; enum { PROP_0, PROP_FOO, PROP_BAR, PROP_BAZ, N_PROPERTIES }; static GParamSpec *properties[N_PROPERTIES] = { NULL, }; G_DEFINE_TYPE (TestObject, test_object, G_TYPE_OBJECT); static void test_object_set_foo (TestObject *obj, gint foo) { if (obj->foo != foo) { obj->foo = foo; g_assert (properties[PROP_FOO] != NULL); g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_FOO]); } } static void test_object_set_bar (TestObject *obj, gboolean bar) { bar = !!bar; if (obj->bar != bar) { obj->bar = bar; g_assert (properties[PROP_BAR] != NULL); g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_BAR]); } } static void test_object_set_baz (TestObject *obj, const gchar *baz) { if (g_strcmp0 (obj->baz, baz) != 0) { g_free (obj->baz); obj->baz = g_strdup (baz); g_assert (properties[PROP_BAZ] != NULL); g_object_notify_by_pspec (G_OBJECT (obj), properties[PROP_BAZ]); } } static void test_object_finalize (GObject *gobject) { g_free (((TestObject *) gobject)->baz); G_OBJECT_CLASS (test_object_parent_class)->finalize (gobject); } static void test_object_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { TestObject *tobj = (TestObject *) gobject; g_assert_cmpint (prop_id, !=, 0); g_assert_cmpint (prop_id, !=, N_PROPERTIES); g_assert (pspec == properties[prop_id]); switch (prop_id) { case PROP_FOO: test_object_set_foo (tobj, g_value_get_int (value)); break; case PROP_BAR: test_object_set_bar (tobj, g_value_get_boolean (value)); break; case PROP_BAZ: test_object_set_baz (tobj, g_value_get_string (value)); break; default: g_assert_not_reached (); } } static void test_object_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { TestObject *tobj = (TestObject *) gobject; g_assert_cmpint (prop_id, !=, 0); g_assert_cmpint (prop_id, !=, N_PROPERTIES); g_assert (pspec == properties[prop_id]); switch (prop_id) { case PROP_FOO: g_value_set_int (value, tobj->foo); break; case PROP_BAR: g_value_set_boolean (value, tobj->bar); break; case PROP_BAZ: g_value_set_string (value, tobj->baz); break; default: g_assert_not_reached (); } } static void test_object_class_init (TestObjectClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); properties[PROP_FOO] = g_param_spec_int ("foo", "Foo", "Foo", -1, G_MAXINT, 0, G_PARAM_READWRITE); properties[PROP_BAR] = g_param_spec_boolean ("bar", "Bar", "Bar", FALSE, G_PARAM_READWRITE); properties[PROP_BAZ] = g_param_spec_string ("baz", "Baz", "Baz", NULL, G_PARAM_READWRITE); gobject_class->set_property = test_object_set_property; gobject_class->get_property = test_object_get_property; gobject_class->finalize = test_object_finalize; g_object_class_install_properties (gobject_class, N_PROPERTIES, properties); } static void test_object_init (TestObject *self) { self->foo = 42; self->bar = TRUE; self->baz = g_strdup ("Hello"); } static void properties_install (void) { TestObject *obj = g_object_new (test_object_get_type (), NULL); GParamSpec *pspec; g_assert (properties[PROP_FOO] != NULL); pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (obj), "foo"); g_assert (properties[PROP_FOO] == pspec); g_object_unref (obj); } typedef struct { const gchar *name; GParamSpec *pspec; } TestNotifyClosure; static void on_notify (GObject *gobject, GParamSpec *pspec, TestNotifyClosure *clos) { g_assert (clos->pspec == pspec); g_assert_cmpstr (clos->name, ==, pspec->name); } static void properties_notify (void) { TestObject *obj = g_object_new (test_object_get_type (), NULL); TestNotifyClosure clos; g_assert (properties[PROP_FOO] != NULL); clos.name = "foo"; clos.pspec = properties[PROP_FOO]; g_signal_connect (obj, "notify", G_CALLBACK (on_notify), &clos); g_object_set (obj, "foo", 47, NULL); g_object_unref (obj); } static void properties_construct (void) { TestObject *obj; gint val; g_test_bug ("630357"); /* more than 16 args triggers a realloc in g_object_new_valist() */ obj = g_object_new (test_object_get_type (), "foo", 1, "foo", 2, "foo", 3, "foo", 4, "foo", 5, "foo", 6, "foo", 7, "foo", 8, "foo", 9, "foo", 10, "foo", 11, "foo", 12, "foo", 13, "foo", 14, "foo", 15, "foo", 16, "foo", 17, "foo", 18, NULL); g_object_get (obj, "foo", &val, NULL); g_assert (val == 18); g_object_unref (obj); } int main (int argc, char *argv[]) { g_type_init (); g_test_init (&argc, &argv, NULL); g_test_bug_base ("http://bugzilla.gnome.org/"); g_test_add_func ("/properties/install", properties_install); g_test_add_func ("/properties/notify", properties_notify); g_test_add_func ("/properties/construct", properties_construct); return g_test_run (); }