1、对象构造
在尝试构建自己的GObjects时人们常常会感到困惑,因为它们有很多不同的方法来实现对象的构造过程:很难确定哪个是正确的方法。
表4中,“g_object_new”显示了在对象实例化过程中调用的用户提供的函数以及它们被调用的顺序。instance_init函数相当于C++中的构造函数。在所有父节点的instance_init函数都被调用后子类的instance_init都将被调用。它不能随意的传递构造参数(如C ++中),但是如果您的对象需要任意参数来完成初始化,则可以使用构造属性。
只有在所有的instance_init函数都运行之后,才能设置构造属性。在所有构造属性设置完成之前,不会将对象引用返回给g_object_new的客户端。
属性ID必须从1开始,因为0被保留供GObject内部使用。
有些人有时需要在传递给构造函数的属性设置完成之后才能完成类型实例的初始化。 这可以通过使用在对象实例化”的部分中描述的constructor()类方法,或者更简单地说,使用compiled()类方法。 请注意,仅在属性标记为G_PARAM_CONSTRUCT_ONLY或G_PARAM_CONSTRUCT时 才会调用compiled()虚函数,且调用发生在传递给g_object_new()的常规属性设置之前。
2、对象析构
在尝试构建自己的GObjects时人们常常会感到困惑,因为它们有很多不同的方法来实现对象的构造过程:很难确定哪个是正确的方法。
表4中,“g_object_new”显示了在对象实例化过程中调用的用户提供的函数以及它们被调用的顺序。instance_init函数相当于C++中的构造函数。在所有父节点的instance_init函数都被调用后子类的instance_init都将被调用。它不能随意的传递构造参数(如C ++中),但是如果您的对象需要任意参数来完成初始化,则可以使用构造属性。
只有在所有的instance_init函数都运行之后,才能设置构造属性。在所有构造属性设置完成之前,不会将对象引用返回给g_object_new的客户端。
重要的是要注意对象构造不能失败。如果您需要一个可能会失败的GObject构造,则可以使用GIO库提供的GInitable和GAsyncInitable接口。
首先应当实现如下代码:
G_DEFINE_TYPE_WITH_PRIVATE (ViewerFile, viewer_file, G_TYPE_OBJECT)
static void
viewer_file_class_init (ViewerFileClass *klass)
{
}
static void
viewer_file_init (ViewerFile *self)
{
ViewerFilePrivate *priv = viewer_file_get_instance_private (self);
/* initialize all public and private members to reasonable default values.
* They are all automatically initialized to 0 to begin with. */
}
如果需要特殊的构造属性(使用G_PARAM_CONSTRUCT_ONLY设置),请在class_init()函数中安装属性函数覆盖GObject类的set_property()和get_property()方法,并按照“对象属性”一节所述实现它们。
属性ID必须从1开始,因为0被保留供GObject内部使用。
enum
{
PROP_FILENAME = 1,
PROP_ZOOM_LEVEL,
N_PROPERTIES
};
static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };
static void
viewer_file_class_init (ViewerFileClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->set_property = viewer_file_set_property;
object_class->get_property = viewer_file_get_property;
obj_properties[PROP_FILENAME] =
g_param_spec_string ("filename",
"Filename",
"Name of the file to load and display from.",
NULL /* default value */,
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE));
obj_properties[PROP_ZOOM_LEVEL] =
g_param_spec_uint ("zoom-level",
"Zoom level",
"Zoom level to view the file at.",
0 /* minimum value */,
10 /* maximum value */,
2 /* default value */,
G_PARAM_READWRITE));
g_object_class_install_properties (object_class,
N_PROPERTIES,
obj_properties);
}
如果需要,请确保您可以构建和运行与上述代码相似的代码。 此外,确保您的构造属性可以在构造期间被设置且没有其他不好的影响。
有些人有时需要在传递给构造函数的属性设置完成之后才能完成类型实例的初始化。 这可以通过使用在对象实例化”的部分中描述的constructor()类方法,或者更简单地说,使用compiled()类方法。 请注意,仅在属性标记为G_PARAM_CONSTRUCT_ONLY或G_PARAM_CONSTRUCT时 才会调用compiled()虚函数,且调用发生在传递给g_object_new()的常规属性设置之前。
2、对象析构
同样,通常很难弄清楚使用哪种机制来实现对象的销毁过程:当最后一次g_object_unref函数调用时,会发生许多事情,如表5“g_object_unref”所述。
您的对象的销毁过程分为两个阶段:dispose和finalize。由于GObject使用的参考计数机制的性质以及销毁序列中的信号发射情况下的临时复现情况,这种分成两个阶段的方法是处理潜在周期的必要条件。有关详细信息,请参阅“引用计数和循环”一节。
struct _ViewerFilePrivate
{
gchar *filename;
guint zoom_level;
GInputStream *input_stream;
};
G_DEFINE_TYPE_WITH_PRIVATE (ViewerFile, viewer_file, G_TYPE_OBJECT)
static void
viewer_file_dispose (GObject *gobject)
{
ViewerFilePrivate *priv = viewer_file_get_instance_private (VIEWER_FILE (gobject));
/* In dispose(), you are supposed to free all types referenced from this
* object which might themselves hold a reference to self. Generally,
* the most simple solution is to unref all members on which you own a
* reference.
*/
/* dispose() might be called multiple times, so we must guard against
* calling g_object_unref() on an invalid GObject by setting the member
* NULL; g_clear_object() does this for us.
*/
g_clear_object (&priv->input_stream);
/* Always chain up to the parent class; there is no need to check if
* the parent class implements the dispose() virtual function: it is
* always guaranteed to do so
*/
G_OBJECT_CLASS (viewer_file_parent_class)->dispose (gobject);
}
static void
viewer_file_finalize (GObject *gobject)
{
ViewerFilePrivate *priv = viewer_file_get_instance_private (VIEWER_FILE (gobject));
g_free (priv->filename);
/* Always chain up to the parent class; as with dispose(), finalize()
* is guaranteed to exist on the parent's class virtual function table
*/
G_OBJECT_CLASS (viewer_file_parent_class)->finalize (gobject);
}
static void
viewer_file_class_init (ViewerFileClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->dispose = viewer_file_dispose;
object_class->finalize = viewer_file_finalize;
}
static void
viewer_file_init (ViewerFile *self);
{
ViewerFilePrivate *priv = viewer_file_get_instance_private (self);
priv->input_stream = g_object_new (VIEWER_TYPE_INPUT_STREAM, NULL);
priv->filename = /* would be set as a property */;
}
在
dispose运行之后finalize运行之前,可能会调用对象方法。 GObject不认为这是一个程序错误:您必须能够正常检测到这一点,既不会崩溃也不会警告用户,通过使一个被
dispose的实例恢复到惰性状态。