基本结构
这部分有模板可以套(30-40行),你只需要把里面的名字换成自己的类的名字, 不过由于存在全大写, 首字母大写,用下划线分割的小写三种情况,所以你需要替换三次。
在这一步,你定义了 6 个宏,两个 struct 和两个函数 xxx_init 和 xxx_class_init.
处理析构
如果你的对象引用了其他对象,或者有动态分配的内存(比如字符串), 那么你就需要处理析构的问题。 gobject 为了解决循环引用的问题,所以析构分为两步, 第一步是解除对其他对象的引用(这一步叫做 dispose), 第二步是释放动态分配的内存(叫做 finialize)。在这一步中,你需要添加一个全局静态变量保存 parent_class, 定义 xxx_dispose 和 xxx_finalize (记得通过 parent_class 调用父类的对应的析构函数), 在 xxx_class_init 代码中初始化 parent_class, 已经把 xxx_dispose 和 xxx_finalize 关联到这个类。
私有成员
封装很重要,所以私有成员不应当放到头文件中,所以类 Xxx 的定义应该如下所示。
struct _Xxx {
GObject parent_instance;
XxxPrivate* priv;
}
gobject 用了一个小技巧来防止生成一个对象时分配两次内存,他可以让生成对象时申请的内存大小为 sizeof(Xxx) + sizeof(XxxPrivate),当然这个是可选的。
通常,你为了支持私有成员,你需要添加一个宏,同时在 xxx_init 和 xxx_class_init 中加入必要的初始化代码。
属性
gobject 的属性主要功能是为了实现事件监听,你可以在属性上挂回调函数来监听属性的改变。如果你不需要的监听的话,那么简单的一个私有成员,再加上 set 和 get 函数即可。实现属性是一件很郁闷的事,你首先要定义一个 enum 来给每个属性一个顺序号, 然后定义 xxx_get_property 和 xxx_set_property 来覆盖基类的实现,然后在 xxx_class_init 中为没个属性定义一个包含属性元数据的结构,最后依次用 g_object_class_install_property 安装。
仅有这些似乎还不够,你一般还需要定义一些辅助函数或辅助宏来降低用户使用的难度,包括 set, get, signal_connect 等。
信号
其实属性的大部分的功能都可以用信号来实现(在属性的 set 函数上 emit 一个 foo-changed 信号),而且 signal 的写法比较简单,无须重写 xxx_set_property 和 xx_get_property。
总之
总之,写的很烦,而且写了半天还没有写到业务代码,每增加一个属性或者信号都需要不少的工作量。看了一下 python 的 gobject 封装感觉很漂亮(见下), gob2 也不错,能让你开发得开心一点。
import gobject
class MyObject(gobject.GObject):
foo = gobject.property(type=str, default='bar')
boolprop = gobject.property(type=bool, default=False)
def __init__(self):
gobject.GObject.__init__(self)
@gobject.property
def readonly(self):
return 'readonly'
最后补一句,虽然 C 语言有些繁琐,但 C 平台真的非常成功,有空再补一下我对平台,库,语言三者关系的观点。