基于 GObject 新写类

基于 GObject 信号的事件响应

基于 GObjet 信号的解决方案大致像下面这样:

void

file_write (File *self, const char *buffer)

{

        /* 向文件写入数据 */

        ... ... ...

        /* 发射“文件改变了”这一信号 */

        g_signal_emit (self, CHANGED, 0);

}

int

main (void)

{

        File *file = file_new ("test.txt");

 

        g_signal_connect (file, "changed", file_print, NULL);

        g_signal_connect (file, "changed", file_print_xml, NULL);

        g_signal_connect (file, "changed", file_print_tex, NULL);

        ... ... ...

}

上述代码的含义如下:

  • 在 file_write 函数中,文件数据写入操作完毕后,就这一事件向外发射一个“CHANGED”信号,告诉所有响应者,文件内容改变了。至于哪些函数是这一信号的响应者,file_write 函数不必知道。
  • file_write 函数的使用者,如果希望哪些函数用于响应 file_write 函数修改文件内容这一事件,那么就使用 g_signal_connect 函数(实际上它是一个宏)将响应函数与信号挂接到一起。这样,一旦事件的对应信号被 g_signal_emit 所发射,这些响应函数便会被自动调用。

为了实现上述的“信号/响应”模拟,那么 file_write 函数的参数便不可能再是 FILE 类型的文件指针了,而是我们自定义的 File 类型的对象,其中封装了“信号/响应”功能。事实上,GObject 类的内部便封装了这些功能,所有经由 GObject 子类化而产生的对象,便可拥有这些功能。

GObject 子类对象的信号处理

首先,我们定义 GObject 子类 MyFile。这个过程,我们应当已经不再陌生,参考文档。

my-file.h 头文件内容如下:

#ifndef MY_FILE_H

#define MY_FILE_H

#include <glib-object.h>

#define MY_TYPE_FILE (my_file_get_type ())

#define MY_FILE(object) G_TYPE_CHECK_INSTANCE_CAST ((object), MY_TYPE_FILE, MyFile)

#define MY_IS_FILE(object) G_TYPE_CHECK_INSTANCE_TYPE ((object), MY_TYPE_FILE))

#define MY_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MY_TYPE_FILE, MyFileClass))

#define MY_IS_FILE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MY_TYPE_FILE))

#define MY_FILE_GET_CLASS(object) (\

                G_TYPE_INSTANCE_GET_CLASS ((object), MY_TYPE_FILE, MyFileClass))

typedef struct _MyFile MyFile;

struct _MyFile {

        GObject parent;

};

typedef struct _MyFileClass MyFileClass;

struct _MyFileClass {

        GObjectClass parent_class;

};

GType my_file_get_type (void);

#endif

my-file.c 源文件内容如下:

#include "my-file.h"

G_DEFINE_TYPE (MyFile, my_file, G_TYPE_OBJECT);

#define MY_FILE_GET_PRIVATE(object) (\

                G_TYPE_INSTANCE_GET_PRIVATE ((object), MY_TYPE_FILE, MyFilePrivate))

typedef struct _MyFilePrivate MyFilePrivate;

struct _MyFilePrivate {

        GString *name;

        GIOChannel *file;

};

enum PropertyDList {

        PROPERTY_FILE_0,

        PROPERTY_FILE_NAME

};

static void

my_file_dispose (GObject *gobject)

{

        MyFile *self        = MY_FILE (gobject);

        MyFilePrivate *priv = MY_FILE_GET_PRIVATE (self);

        if (priv->file){

                g_io_channel_unref (priv->file);

                priv->file = NULL;

        }

        G_OBJECT_CLASS (my_file_parent_class)->dispose (gobject);

}

static void

my_file_finalize (GObject *gobject)

{      

        MyFile *self        = MY_FILE (gobject);

        MyFilePrivate *priv = MY_FILE_GET_PRIVATE (self);

        g_string_free (priv->name, TRUE);

        G_OBJECT_CLASS (my_file_parent_class)->finalize (gobject);

}

static void

my_file_set_property (GObject *object, guint property_id,

                      const GValue *value, GParamSpec *pspec)

{

        MyFile *self = MY_FILE (object);

        MyFilePrivate *priv = MY_FILE_GET_PRIVATE (self);

        switch (property_id){

        case PROPERTY_FILE_NAME:

                if (priv->name)

                        g_string_free (priv->name, TRUE);

                priv->name = g_string_new (g_value_get_string (value));

                priv->file = g_io_channel_new_file (priv->name->str, "a+", NULL);

                break;

        default:

                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);

                break;

        }

}

static void

my_file_get_property (GObject *object, guint property_id,

                      GValue *value, GParamSpec *pspec)

{

        MyFile *self = MY_FILE (object);

        MyFilePrivate *priv = MY_FILE_GET_PRIVATE (self);

        switch (property_id){

        case PROPERTY_FILE_NAME:

                g_value_set_string (value, priv->name->str);

                break;

        default:

                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);

                break;

        }

}

static

void my_file_init (MyFile *self)

{

}

static

void my_file_class_init (MyFileClass *klass)

{

        g_type_class_add_private (klass, sizeof (MyFilePrivate));        

        GObjectClass *base_class = G_OBJECT_CLASS (klass);

        base_class->set_property = my_file_set_property;

        base_class->get_property = my_file_get_property;

        base_class->dispose      = my_file_dispose;

        base_class->finalize     = my_file_finalize;

        GParamSpec *pspec;

        pspec = g_param_spec_string ("name",

                                     "Name",

                                     "File name",

                                     NULL,

                                     G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_CONSTRUCT);

        g_object_class_install_property (base_class, PROPERTY_FILE_NAME, pspec);

        g_signal_new ("file_changed",

                      MY_TYPE_FILE,

                      G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS,

                      0,

                      NULL,

                      NULL,

                      g_cclosure_marshal_VOID__VOID,

                      G_TYPE_NONE,

                      0);

}

void

my_file_write (MyFile *self, gchar *buffer)

{

        MyFilePrivate *priv = MY_FILE_GET_PRIVATE (self);

        g_io_channel_write_chars (priv->file, buffer, -1, NULL, NULL);

        g_io_channel_flush (priv->file, NULL);

        g_signal_emit_by_name(self, "file_changed"); 

}

MyFile 类的使用者——main.c 文件内容如下:

#include "my-file.h"

static void

file_print (gpointer gobject, gpointer user_data)

{      

        g_printf ("invoking file_print!\n");

}

static void

file_print_xml (gpointer gobject, gpointer user_data)

{      

        g_printf ("invoking file_print_xml!\n");

}

static void

file_print_tex (gpointer gobject, gpointer user_data)

{      

        g_printf ("invoking file_print_tex!\n");

}

int

main (void)

{

        g_type_init ();

        MyFile *file = g_object_new (MY_TYPE_FILE, "name", "test.txt", NULL);   

        g_signal_connect (file, "file_changed", G_CALLBACK (file_print), NULL);

        g_signal_connect (file, "file_changed", G_CALLBACK (file_print_xml), NULL);

        g_signal_connect (file, "file_changed", G_CALLBACK (file_print_tex), NULL);

        my_file_write (file, "hello world!\n");

        g_object_unref (file);

        return 0;

}

        首先,是在 MyFile 类的类结构题初始化函数 my_file_class_init 中,除了设置类属性之外,我们调用了 g_signal_new 函数用于建立 MyFile 类型与 "file_changed" 信号的关联。至于究竟是何种关联,那不是我们所关心的!还有,g_signal_new 函数的参数有很多。

        其次,是 MyFile 对象的析构函数。在 my-file.c 源文件中,函数 my_file_dispose 与 my_file_finalize 构成了 MyFile 对象的析构函数,前者用于解除 MyFile 对象对其它对象(是指那些具有引用计数且被 GObject 库的类型系统所管理的对象)的引用,后者用于 MyFile 对象属性的内存释放。至于分何要分为两个阶段进行 GObject 子类对象析构以及相关细节知识,还是另外开一篇文章来讨论吧,否则问题会被越搞越复杂。

猜你喜欢

转载自blog.csdn.net/evsqiezi/article/details/82657479