Gobject tutorial 七

news2024/11/17 16:02:25

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方法。

类的构造方法的作用如下:

  • 通过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;
}

初始化流程如下表

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1839156.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

最新技术:跨境电商源码,应对多国市场需求,让您轻松开展全球业务!

随着全球化进程的不断推进&#xff0c;跨境电商已成为企业拓展国际市场的重要途径。为了满足不同国家和地区消费者不断增长的需求&#xff0c;跨境电商源码应运而生&#xff0c;为企业提供了便捷高效的全球化业务发展方案。 一、全球化运营的关键 跨境电商源码的核心功能在于…

极具吸引力的小程序 UI 风格

极具吸引力的小程序 UI 风格

小白速成AI大模型就看这份资源包

前言 在数字化浪潮席卷全球的今天&#xff0c;人工智能&#xff08;AI&#xff09;技术已成为推动社会进步的重要引擎。尤其是AI大模型&#xff0c;以其强大的数据处理能力和广泛的应用前景&#xff0c;吸引了无数人的目光。然而&#xff0c;对于初学者“小白”来说&#xff0…

ProtoBuf序列化协议简介

首先&#xff0c;常见的序列化方法主要有以下几种&#xff1a; TLV编码及其变体(tag, length, value)&#xff1a; 比如ProtoBuf。文本流编码&#xff1a;XML/JSON固定结构编码&#xff1a;基本原理是&#xff0c;协议约定了传输字段类型和字段含义&#xff0c;和TLV类似&…

MyBatis框架基础

文章目录 1 MyBatis概述2 MyBatis入门2.1 相关依赖2.2 properties配置文件2.3 预编译SQL 3 基本操作3.1 新增操作3.2 删除操作3.3 更新操作3.4 查询操作 4 动态SQL4.1 XML映射文件4.2 if/set/where标签4.3 foreach标签4.4 sql/include标签 5 参考资料 1 MyBatis概述 MyBatis是…

入侵检测系统(IDS)

入侵检测 入侵检测&#xff08;Intrusion Detection&#xff09;是指发现或确定入侵行为存在或出现的动作&#xff0c;也就是发现、跟踪并记录计算机系统或计算机网络中的非授权行为&#xff0c;或发现并调查系统中可能为视图入侵或病毒感染所带来的异常活动。 入侵检测系统 …

XL5300 dTOF测距模块 加镜头后可达7.6米测距距离 ±4%测距精度

XL5300 直接飞行时间&#xff08;dToF&#xff09;传感器是一个整体方案dTOF 模组&#xff0c;应用设计简单。片内集成了单光子雪崩二极管&#xff08;SPAD&#xff09;接收阵列以及VCSEL激光发射器。利用自主研发的 SPAD 和独特的ToF 采集与处理技术&#xff0c;XL5300模块可实…

NV-Embed论文阅读笔记

这是NVIDIA的一篇论文&#xff0c;LLM通常使用的是GPT的decoder范式作为一个生成模型&#xff0c;文章探讨如何利用这样的decoder生成模型来实现BERT这样的encoder的功能&#xff0c;即提取有效的embedding。现有的方法提取embedding的方式无非是 1 mean pooling&#xff1b; 2…

无线领夹麦克风哪个牌子好用?一文揭秘哪种领夹麦性价比最高!

​无线领夹麦克风&#xff0c;无疑是现代音频技术的杰出代表。它摆脱了传统有线麦克风的束缚&#xff0c;让声音的传播更加自由、灵活。无论是追求极致音质的音乐爱好者&#xff0c;还是需要高效沟通的商务人士&#xff0c;无线领夹麦克风都能满足你的需求&#xff0c;让你的声…

HarmonyOS 角落里的知识 —— 状态管理

一、前言 在探索 HarmonyOS 的过程中&#xff0c;我们发现了许多有趣且实用的功能和特性。有些总是在不经意间或者触类旁通的找到。或者是某些开发痛点。其中&#xff0c;状态管理是ArkUI开发非常核心的一个东西&#xff0c;我们进行了大量的使用和测试遇到了许多奇奇怪怪的问…

AD层次原理图绘制

一、在原理图中添加端口 二、添加层次图 三、更新层次图 四、也可以先画层次图&#xff0c;再绘制原理图&#xff0c;这里就不做演示了

天地图 uniapp使用笔记

官网地址&#xff1a;天地图API 效果&#xff1a; <template><view><!-- 显示地图的DOM节点 --><view id"container" class"content"></view><!-- END --><!-- 数据显示 --><h3>城市名称(IP属地)&#x…

关于归一化能量与归一化功率(连续与离散+1)

前言&#xff1a; ① 周期信号 与 直流信号 都是功率信号 ②一个信号可以既不是能量信号也不是功率信号&#xff0c;但不可能既是能量信号又是功率信号 归一化能量 对于一个连续时间信号 ( x(t) )&#xff0c;归一化能量 ( E ) 的定义为&#xff1a; E lim ⁡ T → ∞ ∫ −…

某宝APP参数通过SDK把APP参数转URL参数链接方法

app里参数无法在电脑端或者在没有XX宝的手机里支付, 所以需要转成H5参数进行代付操作 出某xx宝 支付sdk转h5链接方式算法

苹果不会等到明年才对 Siri 进行改进|TodayAI

据彭博社报道&#xff0c;今年苹果&#xff08;APPLE&#xff09;将推出一个更令人满意的 Siri。 当 iOS 18 今年秋季推出时&#xff0c;Siri 的功能不仅仅是让你的 iPhone 边缘显示彩虹光环。虽然苹果智能功能要到 2025 年才会向非测试版用户推出&#xff0c;但据报道&#x…

工作人员能从轧钢测径仪上获取哪些有效信息?

轧钢测径仪安装在轧钢生产线中&#xff0c;无论是热轧还是冷轧&#xff0c;都不能阻挡测径仪的高速无损高精检测。它采用八轴测量系统&#xff0c;能全方位检测外径尺寸&#xff0c;并且配备了测控软件系统&#xff0c;为工作人员提供更加丰富的产线信息。 普通轧钢测径仪能获…

跨境电商必备?揭秘原生IP的作用

一、什么是原生IP&#xff1f; 原生IP&#xff08;Native IP&#xff09;是指由互联网服务提供商&#xff08;ISP&#xff09;或服务器提供商直接分配给用户的IP地址&#xff0c;这种IP地址直接与用户设备或网络相连&#xff0c;也就是指这个IP的注册地址和服务器机房所在的国…

自养号补单:Shopee,Lazada等东南亚电商卖家销量提升的必备技能

在东南亚电商跨境平台中、lazada、shopee是东南亚地区最大的在线购物网站&#xff0c;其目标主要是印地&#xff0c;马来&#xff0c;台湾&#xff0c;菲律宾&#xff0c;新加坡&#xff0c;泰国和越南等用户。而自养号补单作为一种有效的推广手段&#xff0c;正逐渐被越来越多…

安防监控视频平台LntonAIServer视频智能分析平台烟火检测

LntonAIServer烟火检测技术是一种先进的技术&#xff0c;它结合了人工智能的强大能力&#xff0c;专门用于识别和检测烟雾或火焰的存在。这种技术在现代社会的许多领域中都发挥着至关重要的作用&#xff0c;尤其是在安全监控和火警预警系统等关键领域&#xff0c;它的应用更是不…

【PL理论】(33) 类型系统:推导树证明 φ ⊢ e∶t | 继续定义关系:γ ⊢ e∶t

&#x1f4ac; 写在前面&#xff1a;本章我们将讲解推导树证明&#xff0c;推导树实际上就是推理规则的应用。只要学会如何选择并应用适当的推理规则&#xff0c;证明就不是难事了。 目录 0x00 推导树证明 &#x1d753; ⊢ &#x1d486; ∶ &#x1d495; 0x01 继续定义关…