Gobject tutorial 七

The GObject base class

GObject是一个fundamental classed instantiatable type,它的功能如下:

  • 内存管理
  • 构建/销毁实例
  • set/get属性方法
  • 信号
/**
 * GObjectClass:
 * @g_type_class: the parent class
 * @constructor: the @constructor function is called by g_object_new () to 
 *  complete the object initialization after all the construction properties are
 *  set. The first thing a @constructor implementation must do is chain up to the
 *  @constructor of the parent class. Overriding @constructor should be rarely 
 *  needed, e.g. to handle construct properties, or to implement singletons.
 * @set_property: the generic setter for all properties of this type. Should be
 *  overridden for every type with properties. If implementations of
 *  @set_property don't emit property change notification explicitly, this will
 *  be done implicitly by the type system. However, if the notify signal is
 *  emitted explicitly, the type system will not emit it a second time.
 * @get_property: the generic getter for all properties of this type. Should be
 *  overridden for every type with properties.
 * @dispose: the @dispose function is supposed to drop all references to other 
 *  objects, but keep the instance otherwise intact, so that client method 
 *  invocations still work. It may be run multiple times (due to reference 
 *  loops). Before returning, @dispose should chain up to the @dispose method 
 *  of the parent class.
 * @finalize: instance finalization function, should finish the finalization of 
 *  the instance begun in @dispose and chain up to the @finalize method of the 
 *  parent class.
 * @dispatch_properties_changed: emits property change notification for a bunch
 *  of properties. Overriding @dispatch_properties_changed should be rarely 
 *  needed.
 * @notify: the class closure for the notify signal
 * @constructed: the @constructed function is called by g_object_new() as the
 *  final step of the object creation process.  At the point of the call, all
 *  construction properties have been set on the object.  The purpose of this
 *  call is to allow for object initialisation steps that can only be performed
 *  after construction properties have been set.  @constructed implementors
 *  should chain up to the @constructed call of their parent class to allow it
 *  to complete its initialisation.
 * 
 * The class structure for the GObject type.
 * 
 * |[<!-- language="C" -->
 * // Example of implementing a singleton using a constructor.
 * static MySingleton *the_singleton = NULL;
 * 
 * static GObject*
 * my_singleton_constructor (GType                  type,
 *                           guint                  n_construct_params,
 *                           GObjectConstructParam *construct_params)
 * {
 *   GObject *object;
 *   
 *   if (!the_singleton)
 *     {
 *       object = G_OBJECT_CLASS (parent_class)->constructor (type,
 *                                                            n_construct_params,
 *                                                            construct_params);
 *       the_singleton = MY_SINGLETON (object);
 *     }
 *   else
 *     object = g_object_ref (G_OBJECT (the_singleton));
 * 
 *   return object;
 * }
 * ]|
 */
struct  _GObjectClass
{
  GTypeClass   g_type_class;

  /*< private >*/
  GSList      *construct_properties;

  /*< public >*/
  /* seldom overridden */
  GObject*   (*constructor)     (GType                  type,
                                 guint                  n_construct_properties,
                                 GObjectConstructParam *construct_properties);
  /* overridable methods */
  void       (*set_property)		(GObject        *object,
                                         guint           property_id,
                                         const GValue   *value,
                                         GParamSpec     *pspec);
  void       (*get_property)		(GObject        *object,
                                         guint           property_id,
                                         GValue         *value,
                                         GParamSpec     *pspec);
  void       (*dispose)			(GObject        *object);
  void       (*finalize)		(GObject        *object);
  /* seldom overridden */
  void       (*dispatch_properties_changed) (GObject      *object,
					     guint	   n_pspecs,
					     GParamSpec  **pspecs);
  /* signals */
  void	     (*notify)			(GObject	*object,
					 GParamSpec	*pspec);

  /* called when done constructing */
  void	     (*constructed)		(GObject	*object);

  /*< private >*/
  gsize		flags;

  gsize         n_construct_properties;

  gpointer pspecs;
  gsize n_pspecs;

  /* padding */
  gpointer	pdummy[3];
};

/**
 * GObject:
 *
 * The base object type.
 * 
 * All the fields in the `GObject` structure are private to the implementation
 * and should never be accessed directly.
 *
 * Since GLib 2.72, all #GObjects are guaranteed to be aligned to at least the
 * alignment of the largest basic GLib type (typically this is #guint64 or
 * #gdouble). If you need larger alignment for an element in a #GObject, you
 * should allocate it on the heap (aligned), or arrange for your #GObject to be
 * appropriately padded. This guarantee applies to the #GObject (or derived)
 * struct, the #GObjectClass (or derived) struct, and any private data allocated
 * by G_ADD_PRIVATE().
 */
struct  _GObject
{
  GTypeInstance  g_type_instance;
  
  /*< private >*/
  guint          ref_count;  /* (atomic) */
  GData         *qdata;
};

Object instantiation

g_object_new()族能够实例化任何继承自GObject的GType。族中所有函数都能保证将类结构和实例结构在GLib的类型系统中正确初始化,之后会在某个时机调用类的constructor方法。

类的constructor方法的作用如下:

  • 通过g_type_create_instance()函数分配并清空内存。
  • 使用构造属性初始化对象实例。
static GObject*
g_object_constructor (GType                  type,
		      guint                  n_construct_properties,
		      GObjectConstructParam *construct_params)
{
  GObject *object;

  /* create object */
  object = (GObject*) g_type_create_instance (type);
  
  /* set construction parameters */
  if (n_construct_properties)
    {
      GObjectNotifyQueue *nqueue = g_object_notify_queue_freeze (object, FALSE);
      
      /* set construct properties */
      while (n_construct_properties--)
	{
	  GValue *value = construct_params->value;
	  GParamSpec *pspec = construct_params->pspec;

	  construct_params++;
	  object_set_property (object, pspec, value, nqueue, TRUE);
	}
      g_object_notify_queue_thaw (object, nqueue);
      /* the notification queue is still frozen from g_object_init(), so
       * we don't need to handle it here, g_object_newv() takes
       * care of that
       */
    }

  return object;
}

GObject能够确保所有类结构和实例结构的成员(除指向父结构的成员外)的值都被设置为0。

当所有构造相关的工作都完成,构造属性都被设置完成后,最后会调用constructed方法。

继承自GObject的对象都能重写类的constructed方法。举例如下:

#define VIEWER_TYPE_FILE viewer_file_get_type ()
G_DECLARE_FINAL_TYPE (ViewerFile, viewer_file, VIEWER, FILE, GObject)

struct _ViewerFile
{
  GObject parent_instance;

  /* instance members */
  char *filename;
  guint zoom_level;
};

/* will create viewer_file_get_type and set viewer_file_parent_class */
G_DEFINE_TYPE (ViewerFile, viewer_file, G_TYPE_OBJECT)

static void
viewer_file_constructed (GObject *obj)
{
  /* update the object state depending on constructor properties */

  /* Always chain up to the parent constructed function to complete object
   * initialisation. */
  G_OBJECT_CLASS (viewer_file_parent_class)->constructed (obj);
}

static void
viewer_file_finalize (GObject *obj)
{
  ViewerFile *self = VIEWER_FILE (obj);

  g_free (self->filename);

  /* Always chain up to the parent finalize function to complete object
   * destruction. */
  G_OBJECT_CLASS (viewer_file_parent_class)->finalize (obj);
}

static void
viewer_file_class_init (ViewerFileClass *klass)
{
  GObjectClass *object_class = G_OBJECT_CLASS (klass);

  object_class->constructed = viewer_file_constructed;
  object_class->finalize = viewer_file_finalize;
}

static void
viewer_file_init (ViewerFile *self)
{
  /* initialize the object */
}

 通过g_object_new(VIEWER_TYPE_FILE,NULL)的方式,第一次实例化ViewerFile对象时,会调用view_file_base_init函数,接着调用view_file_class_init函数。这样新对象的类结构就能够被初始化完成。

当g_object_new获取到新对象的初始化完成的类结构的索引之后,如果GObject的constructor函数被重写,那么,g_object_new函数将会调用新类型的类结构中constructor(如上例中的viewer_file_cosntructed,实际上也是新类ViewFileClass中GObject的constructor,这是因为,ViewFile是一个final类型对象)来创建新对象。

gpointer
g_object_new (GType	   object_type,
	      const gchar *first_property_name,
	      ...)
{
  GObject *object;
......
  
  /* short circuit for calls supplying no properties */
  if (!first_property_name)
    return g_object_new_with_properties (object_type, 0, NULL, NULL);

......
  
  return object;
}


GObject *
g_object_new_with_properties (GType          object_type,
                              guint          n_properties,
                              const char    *names[],
                              const GValue   values[])
{
  GObjectClass *class, *unref_class = NULL;
  GObject *object;
......


  class = g_type_class_peek_static (object_type);

  if (class == NULL)
    class = unref_class = g_type_class_ref (object_type);

  if (n_properties > 0)
    {
......
    }
  else
    object = g_object_new_internal (class, NULL, 0);
......

  return object;
}

static gpointer
g_object_new_internal (GObjectClass          *class,
                       GObjectConstructParam *params,
                       guint                  n_params)
{
......
  GObject *object;
......

  if G_UNLIKELY (CLASS_HAS_CUSTOM_CONSTRUCTOR (class))
    return g_object_new_with_custom_constructor (class, params, n_params);

  object = (GObject *) g_type_create_instance (class->g_type_class.g_type);

......
}

static gpointer
g_object_new_with_custom_constructor (GObjectClass          *class,
                                      GObjectConstructParam *params,
                                      guint                  n_params)
{
......
  GObject *object;

......

  /* construct object from construction parameters */
  object = class->constructor (class->g_type_class.g_type, class->n_construct_properties, cparams);
 
......
return object
}

Chaining up to its parent

在继承Gobject的新类型的初始化过程中,如上所述,会存在GObjectClass的constructor被用户改写的情况,那么,用户为新对象设置的constructor与GObjectClass的默认constructor之间是什么关系呢?

如下图所示:

 如图,有两个类结构,GObjectClass和TstrClass,两个类都有各自的finalize函数,TstrClass的finalize函数用于销毁Tstr 实例结构中与其本身相关的数据(不包含Tstr结构中其父结构GObjectClass中的数据),TStrClass的finalize函数在最后会调用其父类GObjectClass的finalize函数来销毁GObject中的数据。这期间有个函数调用关系,这个调用关系就是图示“chain up”的含义。

我们在gtk库中找个finalize函数来具体说明一下。

static void
gtk_print_backend_cups_finalize (GObject *object)
{
  GtkPrintBackendCups *backend_cups;

  GTK_DEBUG (PRINTING, "CUPS Backend: finalizing CUPS backend module");

  backend_cups = GTK_PRINT_BACKEND_CUPS (object);

  g_free (backend_cups->default_printer);
  backend_cups->default_printer = NULL;

  gtk_cups_connection_test_free (backend_cups->cups_connection_test);
  backend_cups->cups_connection_test = NULL;

  g_hash_table_destroy (backend_cups->auth);

  g_free (backend_cups->username);

#ifdef HAVE_COLORD
  g_object_unref (backend_cups->colord_client);
#endif

  g_clear_object (&backend_cups->avahi_cancellable);
  g_clear_pointer (&backend_cups->avahi_default_printer, g_free);
  g_clear_object (&backend_cups->dbus_connection);

  g_clear_object (&backend_cups->secrets_service_cancellable);
  if (backend_cups->secrets_service_watch_id != 0)
    {
      g_bus_unwatch_name (backend_cups->secrets_service_watch_id);
    }

  g_list_free_full (backend_cups->temporary_queues_in_construction, g_free);
  backend_cups->temporary_queues_in_construction = NULL;

  g_list_free_full (backend_cups->temporary_queues_removed, g_free);
  backend_cups->temporary_queues_removed = NULL;

  backend_parent_class->finalize (object);
}

 现在我们可以回到我们之前的问题,类似于finalize,constructor也有“chain up“的关系存在。

同样的,我们在gtk库中找一个constructor函数看看。

static GObject*
gtk_button_constructor (GType                  type,
			guint                  n_construct_properties,
			GObjectConstructParam *construct_params)
{
  GObject *object;
  GtkButton *button;

  object = (* G_OBJECT_CLASS (gtk_button_parent_class)->constructor) (type,
								      n_construct_properties,
								      construct_params);

  button = GTK_BUTTON (object);
  button->constructed = TRUE;

  if (button->label_text != NULL)
    gtk_button_construct_child (button);
  
  return object;
}

回到我们之前的话题,在初始化过程中,通过"chain up"会调用到g_object_constructor,只不过constructor的调用顺序与finalize相反。因为,我们需要g_object_constructor函数调用g_type_create_instance来为我们的新类型实例分配空间。同时,instance_init函数也会在此时执行。instance_init的返回,也意味这新类型的初始化过程完成。之后,用户就能使用新类型。

GTypeInstance*
g_type_create_instance (GType type)
{
  TypeNode *node;
  GTypeInstance *instance;
  GTypeClass *class;
  gchar *allocated;
  gint private_size;
  gint ivar_size;
  guint i;

  node = lookup_type_node_I (type);
  
  ......

  class = g_type_class_ref (type);

  /* We allocate the 'private' areas before the normal instance data, in
   * reverse order.  This allows the private area of a particular class
   * to always be at a constant relative address to the instance data.
   * If we stored the private data after the instance data this would
   * not be the case (since a subclass that added more instance
   * variables would push the private data further along).
   *
   * This presents problems for valgrindability, of course, so we do a
   * workaround for that case.  We identify the start of the object to
   * valgrind as an allocated block (so that pointers to objects show up
   * as 'reachable' instead of 'possibly lost').  We then add an extra
   * pointer at the end of the object, after all instance data, back to
   * the start of the private area so that it is also recorded as
   * reachable.  We also add extra private space at the start because
   * valgrind doesn't seem to like us claiming to have allocated an
   * address that it saw allocated by malloc().
   */
  private_size = node->data->instance.private_size;
  ivar_size = node->data->instance.instance_size;

  ......

  allocated = g_malloc0 (private_size + ivar_size);

  instance = (GTypeInstance *) (allocated + private_size);

  for (i = node->n_supers; i > 0; i--)
    {
      TypeNode *pnode;
      
      pnode = lookup_type_node_I (node->supers[i]);
      if (pnode->data->instance.instance_init)
	{
	  instance->g_class = pnode->data->instance.class;
	  pnode->data->instance.instance_init (instance, class);
	}
    }
  ......

  instance->g_class = class;
  if (node->data->instance.instance_init)
    node->data->instance.instance_init (instance, class);



  return instance;
}

初始化流程如下表

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值