GObject 参考手册:概念:基类 GObject

The two previous chapters discussed the details of GLib’s Dynamic Type System and its signal control system. The GObject library also contains an implementation for a base fundamental type named GObject.
前面两个章节讨论了Glib动态类型系统的细节和它的信号控制系统。GObject库同时也包括了一个最基本的类型即基类,即为GObject。

GObject is a fundamental classed instantiable type. It implements:
GObject是一个类化的可实例的基类。它实现了:

  • Memory management with reference counting
  • 使用引用计数的内存管理。
  • Construction/Destruction of instances
  • 实例的构造和析构 。
  • Generic per-object properties with set/get function pairs
  • 使用set/get函数对进行的一般性属性操作。
  • Easy use of signals
  • 简易使用的信号机制。

All the GNOME libraries which use the GLib type system (like GTK+ and GStreamer) inherit from GObject which is why it is important to understand the details of how it works.
所有使用GLib类型系统的GNOME库(如GTK+和GStreamer)都由GObject继承,所以了解有关它是如何工作的细节是如此重要。

Object instantiation
对象实例

The g_object_new family of functions can be used to instantiate any GType which inherits from the GObject base type. All these functions make sure the class and instance structures have been correctly initialized by glib’s type system and then invoke at one point or another the constructor class method which is used to:
g_object_new系列函数可以被用作实例化由GObject基本类型继承的一些GType。所有的这些函数确保类和实例的结构被Glib的类型系统正确的初始化,然后调用一个或别的构造类方法用于:

  • Allocate and clear memory through g_type_create_instance.
  • 通过g_type_create_instance来分配和清理内存。
  • Initialize the object’ instance with the construction properties.
  • 初始化对象的实例用构造属性。

Although one can expect all class and instance members (except the fields pointing to the parents) to be set to zero, some consider it good practice to explicitly set them.
尽管你可以期待所有的类和实例成员(指向父类的部分)都会设置为0,但是尽量考虑自己明确地去设置它们。

Objects which inherit from GObject are allowed to override this constructor class method: they should however chain to their parent constructor method before doing so:
由GObject继承的对象们允许重载它的类构造方法:在做下面之前它们需要连接到父类的构造方法:

GObject*   (*constructor)     (GType                  type,
                                 guint                  n_construct_properties,
                                 GObjectConstructParam *construct_properties);

The example below shows how MamanBar overrides the parent’s constructor:
下面的代码显示MamanBar重载了父类的构造器:

#define MAMAN_TYPE_BAR                  (maman_bar_get_type ())
#define MAMAN_BAR(obj)                  (G_TYPE_CHECK_INSTANCE_CAST ((obj), MAMAN_TYPE_BAR, MamanBar))
#define MAMAN_BAR_CLASS(klass)          (G_TYPE_CHECK_CLASS_CAST ((klass), MAMAN_TYPE_BAR, MamanBarClass))
#define MAMAN_IS_BAR(obj)          (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MAMAN_TYPE_BAR))
#define MAMAN_IS_BAR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MAMAN_TYPE_BAR))
#define MAMAN_BAR_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), MAMAN_TYPE_BAR, MamanBarClass))
 
typedef struct _MamanBar MamanBar;
typedef struct _MamanBarClass MamanBarClass;
 
struct _MamanBar {
  GObject parent;
  /* instance members */
};
 
struct _MamanBarClass {
  GObjectClass parent;
 
  /* class members */
};
 
/* used by MAMAN_TYPE_BAR */
GType maman_bar_get_type (void);
 
static GObject *
maman_bar_constructor (GType                  type,
                       guint                  n_construct_properties,
                       GObjectConstructParam *construct_properties)
{
  GObject *obj;
 
  {
    /* Invoke parent constructor. */
    MamanBarClass *klass;
    GObjectClass *parent_class;  
    klass = MAMAN_BAR_CLASS (g_type_class_peek (MAMAN_TYPE_BAR));
    parent_class = G_OBJECT_CLASS (g_type_class_peek_parent (klass));
    obj = parent_class->constructor (type,
                                     n_construct_properties,
                                     construct_properties);
  }
 
  /* do stuff. */
 
  return obj;
}
 
static void
maman_bar_instance_init (GTypeInstance   *instance,
                         gpointer         g_class)
{
  MamanBar *self = (MamanBar *)instance;
  /* do stuff */
}
 
static void
maman_bar_class_init (gpointer g_class,
                      gpointer g_class_data)
{
  GObjectClass *gobject_class = G_OBJECT_CLASS (g_class);
  MamanBarClass *klass = MAMAN_BAR_CLASS (g_class);
 
  gobject_class->constructor = maman_bar_constructor;
}
 
GType maman_bar_get_type (void)
{
  static GType type = 0;
  if (type == 0) {
    static const GTypeInfo info = {
      sizeof (MamanBarClass),
      NULL,   /* base_init */
      NULL,   /* base_finalize */
      maman_bar_class_init,   /* class_init */
      NULL,   /* class_finalize */
      NULL,   /* class_data */
      sizeof (MamanBar),
      0,      /* n_preallocs */
      maman_bar_instance_init    /* instance_init */
    };
    type = g_type_register_static (G_TYPE_OBJECT,
                                   "MamanBarType",
                                   &info, 0);
  }
  return type;
}

If the user instantiates an object MamanBar with:
如果用户是用下面的方法创建一个MamanBar的实例的:

MamanBar *bar = g_object_new (MAMAN_TYPE_BAR, NULL);

If this is the first instantiation of such an object, the maman_b_class_init function will be invoked after any maman_b_base_class_init function. This will make sure the class structure of this new object is correctly initialized. Here, maman_bar_class_init is expected to override the object’s class methods and setup the class’ own methods. In the example above, the constructor method is the only overridden method: it is set to maman_bar_constructor.
如果这是用户创建的第一个实例,那么maman_b_class_init函数将会在maman_b_base_class_init 调用后才调用。这将确保这个新对象的类结构会正确的初始化。这里,mana_bar_class_init被期望重载对象的类方法,并且设置这个类的自己的方法。在上面的例子中,构造方法是唯一被重载的方法:它设置为了maman_bar_constructor。

Once g_object_new has obtained a reference to an initialized class structure, it invokes its constructor method to create an instance of the new object. Since it has just been overridden by maman_bar_class_init to maman_bar_constructor, the latter is called and, because it was implemented correctly, it chains up to its parent’s constructor. The problem here is how we can find the parent constructor. An approach (used in GTK+ source code) would be to save the original constructor in a static variable from maman_bar_class_init and then to re-use it from maman_bar_constructor. This is clearly possible and very simple but I was told it was not nice and the preferred way is to use the g_type_class_peek and g_type_class_peek_parent functions.
一旦g_object_new已经得到了一个初始化后的类结构的引用,它就调用它的构造方法来为这个新对象创建一个实例。因为它刚刚被 maman_bar_class_init由maman_bar_constructor重载,后者将被调用,因为它是实现正确的,它也连接了父类的构造器。问题是,我们怎么才能找到父类的构造器。一个接近(在GTK+源代码中使用)将会来保存原始的构造器在一个从maman_class_init来的静态变量中,然后可以通过maman_bar_constructor来重用它。这是足够清楚的,也非常简单,但是我说这不是很好的办法,更好的办法是用 g_type_class_peek和g_type_class_peek_parent函数。

Finally, at one point or another, g_object_constructor is invoked by the last constructor in the chain. This function allocates the object’s instance’ buffer through g_type_create_instance which means that the instance_init function is invoked at this point if one was registered. After instance_init returns, the object is fully initialized and should be ready to answer any user-request. When g_type_create_instance returns, g_object_constructor sets the construction properties (i.e. the properties which were given to g_object_new) and returns to the user’s constructor which is then allowed to do useful instance initialization…
最终,在长链中一个g_object_constructor被最后一个构造器调用。这个函数通过g_type_create_instance分配的对象实例的缓存,这意味着如果它被注册过的话instance_init函数将在这点上被调用。在instance_init返回以后,对象就完全初始化好了,应该准备好应答用户的请求了。当g_type_create_instance返回了,g_object_constructor设置构造属性(属性由g_object_new给的)并返回用户的构造器,使它来允许完成一些有用的实例初始化。

The process described above might seem a bit complicated (it is actually overly complicated in my opinion..) but it can be summarized easily by the table below which lists the functions invoked by g_object_new and their order of invocation.
这个所描述的处理过程看起来有一点难懂(在我看来确实难懂),但是由下面的表格可以清楚的概况起来,当g_object_new调用时,相关函数的调用顺序。

The array below lists the functions invoked by g_object_new and their order of invocation:
箭头表示由g_object_new进行的函数调用和它们的调用顺序。

Invocation time Function Invoked Function’s parameters Remark
First call to g_object_new for target type target type’s base_init function On the inheritance tree of classes from fundamental type to target type. base_init is invoked once for each class structure. I have no real idea on how this can be used. If you have a good real-life example of how a class’ base_init can be used, please, let me know.
target type’s class_init function On target type’s class structure Here, you should make sure to initialize or override class methods (that is, assign to each class’ method its function pointer) and create the signals and the properties associated to your object.
interface’ base_init function On interface’ vtable
interface’ interface_init function On interface’ vtable
Each call to g_object_new for target type target type’s class constructor method: GObjectClass->constructor On object’s instance If you need to complete the object initialization after all the construction properties are set, override the constructor method and make sure to chain up to the object’s parent class before doing your own initialization. In doubt, do not override the constructor method.
type’s instance_init function On the inheritance tree of classes from fundamental type to target type. the instance_init provided for each type is invoked once for each instance structure. Provide an instance_init function to initialize your object before its construction properties are set. This is the preferred way to initialize a GObject instance. This function is equivalent to C++ constructors.

Readers should feel concerned about one little twist in the order in which functions are invoked: while, technically, the class’ constructor method is called before the GType’s instance_init function (since g_type_create_instance which calls instance_init is called by g_object_constructor which is the top-level class constructor method and to which users are expected to chain to), the user’s code which runs in a user-provided constructor will always run after GType’s instance_init function since the user-provided constructor must (you’ve been warned) chain up before doing anything useful.
读者可能会对函数调用的顺序感到不安:当然,技术上来说,类结构方法总是在GType的instance_init调用之前被调用(因为g_type_create_instance来调用 instance_init相当于由g_object_constructor来调用顶级类的构造器,这是用户所期望的),运行着用户提供的构造器的用户的代码将总是在GType的instance_init函数之后运行,因为用户提供的构造器必须在做一些有用的事情前被链接好。

Leave a Reply

Your email address will not be published. Required fields are marked *