本节将介绍接口背后的理论。 如何定义和实现接口详见 How to define and implement interfaces章节。
例如,下面代码注册一个实现ViewerEditable的新的ViewerFile类:
首先分配一个内存缓冲区来保存接口结构。父级的接口结构然后被复制到新的接口结构中(父接口已经在那时被初始化了)。如果没有父接口,接口结构将使用零初始化。g_type和g_instance_type字段随后被初始化:g_type被设置为最传导接口的类型,并且g_instance_type被设置为实现该接口的最导出类型的类型。
调用接口base_init,然后调用接口的default_init。最后,如果类型已经注册了该接口的实现,则会调用该实现的interface_init函数。如果有一个接口的多个实现,对于初始化的每个实现,base_init和interface_init函数都将被调用一次。因此建议使用default_init函数来初始化一个接口。该函数只会被调用一次,无论这个接口有多少个实现。default_init函数由G_DEFINE_INTERFACE声明,可用于定义接口:
接口销毁
当注册接口的一个可实例化类型的最后一个实例被销毁时,与该类型关联的接口实现被会被销毁。要破坏接口实现,GType首先调用实现的interface_finalize函数,然后再调用接口最传统的base_finalize函数。
同样重要的是要理解,如在“接口初始化”一节中,interface_finalize和base_finalize都被调用一次,以便破坏每个接口的实现。
因此,如果要使用这些函数之一,则需要使用静态整数变量,该变量将保存接口实现的实例数,以使接口的类仅被破坏一次(当整数变量达到零时)。
上述过程可概括如下:
GType的接口非常类似于Java的接口。 它们允许设计一个通用接口,几个类将遵循这个接口。想象一下,高保真设备上的播放,暂停和停止按钮 - 可以看作播放界面。一旦你知道他们做了什么,你可以控制你的CD播放器,MP3播放器或任何使用这些符号的东西。要声明一个接口,你必须注册一个派生自GTypeInterface的不可实例化的类类型。 以下代码段声明了这样一个接口。
#define VIEWER_TYPE_EDITABLE viewer_editable_get_type ()
G_DECLARE_INTERFACE (ViewerEditable, viewer_editable, VIEWER, EDITABLE, GObject)
struct _ViewerEditableInterface {
GTypeInterface parent;
void (*save) (ViewerEditable *self,
GError **error);
};
void viewer_editable_save (ViewerEditable *self,
GError **error);
viewer_editable_get_type注册一个名为ViewerEditable的类型,它继承自G_TYPE_INTERFACE。 所有接口必须是继承树中G_TYPE_INTERFACE的子节点。一个接口必须由一个包含GTypeInterface结构的作为第一个成员的结构来定义。接口结构期望包含接口方法的函数指针。为每个简单地直接调用接口方法的接口方法定义帮助函数是很好的风格:viewer_editable_save是其中之一。如果没有特殊要求,可以使用G_IMPLEMENT_INTERFACE宏来实现一个接口:
static void
viewer_file_save (ViewerEditable *self)
{
g_print ("File implementation of editable interface save method.\n");
}
static void
viewer_file_editable_interface_init (ViewerEditableInterface *iface)
{
iface->save = viewer_file_save;
}
G_DEFINE_TYPE_WITH_CODE (ViewerFile, viewer_file, VIEWER_TYPE_FILE,
G_IMPLEMENT_INTERFACE (VIEWER_TYPE_EDITABLE,
viewer_file_editable_interface_init));
如果你的代码有特殊的要求,你必须编写一个自定义的get_type函数来注册你的GType,它继承自一些GObject,并且实现了接口ViewerEditable。
例如,下面代码注册一个实现ViewerEditable的新的ViewerFile类:
static void
viewer_file_save (ViewerEditable *editable)
{
g_print ("File implementation of editable interface save method.\n");
}
static void
viewer_file_editable_interface_init (gpointer g_iface,
gpointer iface_data)
{
ViewerEditableInterface *iface = g_iface;
iface->save = viewer_file_save;
}
GType
viewer_file_get_type (void)
{
static GType type = 0;
if (type == 0) {
const GTypeInfo info = {
sizeof (ViewerFileClass),
NULL, /* base_init */
NULL, /* base_finalize */
NULL, /* class_init */
NULL, /* class_finalize */
NULL, /* class_data */
sizeof (ViewerFile),
0, /* n_preallocs */
NULL /* instance_init */
};
const GInterfaceInfo editable_info = {
(GInterfaceInitFunc) viewer_file_editable_interface_init, /* interface_init */
NULL, /* interface_finalize */
NULL /* interface_data */
};
type = g_type_register_static (VIEWER_TYPE_FILE,
"ViewerFile",
&info, 0);
g_type_add_interface_static (type,
VIEWER_TYPE_EDITABLE,
&editable_info);
}
return type;
}
给定类型实现的类型系统中的g_type_add_interface_static记录也是FooInterface(foo_interface_get_type返回FooInterface的类型)。 GInterfaceInfo结构保存有关接口实现的信息:
struct _GInterfaceInfo
{
GInterfaceInitFunc interface_init;
GInterfaceFinalizeFunc interface_finalize;
gpointer interface_data;
};
当第一次创建实现接口(直接或通过继承超类的实现)的实例化类时,其类结构将按照“Instantiable classed types: objects”一节中描述的过程进行初始化。之后,与该类型相关联的接口实现被初始化。
首先分配一个内存缓冲区来保存接口结构。父级的接口结构然后被复制到新的接口结构中(父接口已经在那时被初始化了)。如果没有父接口,接口结构将使用零初始化。g_type和g_instance_type字段随后被初始化:g_type被设置为最传导接口的类型,并且g_instance_type被设置为实现该接口的最导出类型的类型。
调用接口base_init,然后调用接口的default_init。最后,如果类型已经注册了该接口的实现,则会调用该实现的interface_init函数。如果有一个接口的多个实现,对于初始化的每个实现,base_init和interface_init函数都将被调用一次。因此建议使用default_init函数来初始化一个接口。该函数只会被调用一次,无论这个接口有多少个实现。default_init函数由G_DEFINE_INTERFACE声明,可用于定义接口:
G_DEFINE_INTERFACE (ViewerEditable, viewer_editable, G_TYPE_OBJECT);
static void
viewer_editable_default_init (ViewerEditableInterface *iface)
{
/* add properties and signals here, will only be called once */
}
或者您可以自己实现在自己的GType函数中:
viewer_editable_get_type (void)
{
static volatile gsize type_id = 0;
if (g_once_init_enter (&type_id)) {
const GTypeInfo info = {
sizeof (ViewerEditableInterface),
NULL, /* base_init */
NULL, /* base_finalize */
viewer_editable_default_init, /* class_init */
NULL, /* class_finalize */
NULL, /* class_data */
0, /* instance_size */
0, /* n_preallocs */
NULL /* instance_init */
};
GType type = g_type_register_static (G_TYPE_INTERFACE,
"ViewerEditable",
&info, 0);
g_once_init_leave (&type_id, type);
}
return type_id;
}
static void
viewer_editable_default_init (ViewerEditableInterface *iface)
{
/* add properties and signals here, will only called once */
}
Table 2. Interface Initialization
Invocation time | Function Invoked | Function's parameters | Remark |
---|---|---|---|
First call to g_type_create_instance for any type implementing interface | interface's base_init function | On interface's vtable | Rarely necessary to use this. Called once per instantiated classed type implementing the interface. |
First call to g_type_create_instance for each type implementing interface | interface's default_init function | On interface's vtable | Register interface's signals, properties, etc. here. Will be called once. |
First call to g_type_create_instance for any type implementing interface | implementation's interface_init function | On interface's vtable | Initialize interface implementation. Called for each class that that implements the interface. Initialize the interface method pointers in the interface structure to the implementing class's implementation. |
当注册接口的一个可实例化类型的最后一个实例被销毁时,与该类型关联的接口实现被会被销毁。要破坏接口实现,GType首先调用实现的interface_finalize函数,然后再调用接口最传统的base_finalize函数。
同样重要的是要理解,如在“接口初始化”一节中,interface_finalize和base_finalize都被调用一次,以便破坏每个接口的实现。
因此,如果要使用这些函数之一,则需要使用静态整数变量,该变量将保存接口实现的实例数,以使接口的类仅被破坏一次(当整数变量达到零时)。
上述过程可概括如下:
Table 3. Interface Finalization
Invocation time | Function Invoked | Function's parameters |
---|---|---|
Last call to g_type_free_instance for type implementing interface | interface's interface_finalize function | On interface's vtable |
interface's base_finalize function | On interface's vtable |