Gobject tutorial 八

The GObject base class

Object memory management

Gobject的内存管理相关的API很复杂,但其目标是提供一个基于引用计数的灵活的内存管理模式。

下面我们来介绍一下,与管理引用计数相关的函数。

Reference Count

函数g_object_ref和g_object_unref的作用分别是增加和减少引用计数。g_clear_object函数是对g_object_unref的封装。

在调用g_object_new时,引用计数是1,1代表到目前为止使用此函数的使用者是此对象的唯一使用者,当引用计数是0时,对象的类结构体中的disopose()和finalize()会被调用,之后会调用g_type_free_instance()来释放对象实例结构体所占用的空间或者返回空间到此类型对象的对象池。如果对象的最后一个实例被释放,那么,对象的类结构体也会被销毁。

GObject销毁流程如下表:

Weak Reference

weak reference 的作用是监视对象的终止(finalization),g_object_weak_ref()会添加一个监视回调函数到对象,回调函数不会使对象的引用计数有任何变化,它只是在对象运行dispose的时候执行。

 Reference counts and cycles

GObject的销毁过程分为两个阶段,第一个阶段是在dispose函数中,dispose()会释放所有对其它成员对象的引用。第二个阶段是在finalize函数中,finalize()会释放对象本身所占用空间。在调用dispose后,调用finalize之前,对象的方法还能正常执行。

销毁过程分为两个阶段有益于打破循环引用。当检测到循环引用时,为了正常进行销毁工作,调用g_object_run_dipose就能打破循环。dispose能被多次执行,finalize只会执行一次。现在我们举例对这几句话进行说明。假如,对象A和对象B相互引用,现在我们检测到这样的情况,那么,现在需要销毁这两个对象,该怎么办呢?一种方式就是应用程序在操作释放一个对象时,调用g_object_run_dispose。

如果对象A全部释放所有对其他引用,当然也会释放对对象B的引用。如果这是最后一个对对象B的引用,那么A释放对B的引用,会导致unref 函数执行对象B的dispose, 进而导致B对象释放对对象A的引用,如果这也是对对象A的最后一个引用,对象B对对象A的引用的释放会导致第二次执行A的dispose。

Object properties

GObject的一个功能是能够通过set_property/get_property函数对对象属性进行操作。首先我们先举例说明属性如何定义的,以及在应用程序中如何使用。

首先,看一下定义。

// Implementationtypedef enum
{PROP_FILENAME = 1,PROP_ZOOM_LEVEL,N_PROPERTIES
} ViewerFileProperty;static GParamSpec *obj_properties[N_PROPERTIES] = { NULL, };static void
viewer_file_set_property (GObject      *object,guint         property_id,const GValue *value,GParamSpec   *pspec)
{ViewerFile *self = VIEWER_FILE (object);switch ((ViewerFileProperty) property_id){case PROP_FILENAME:g_free (self->filename);self->filename = g_value_dup_string (value);g_print ("filename: %s\n", self->filename);break;case PROP_ZOOM_LEVEL:self->zoom_level = g_value_get_uint (value);g_print ("zoom level: %u\n", self->zoom_level);break;default:/* We don't have any other property... */G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);break;}
}static void
viewer_file_get_property (GObject    *object,guint       property_id,GValue     *value,GParamSpec *pspec)
{ViewerFile *self = VIEWER_FILE (object);switch ((ViewerFileProperty) property_id){case PROP_FILENAME:g_value_set_string (value, self->filename);break;case PROP_ZOOM_LEVEL:g_value_set_uint (value, self->zoom_level);break;default:/* We don't have any other property... */G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);break;}
}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);
}

再看看如何在应用中使用属性。

// UseViewerFile *file;
GValue val = G_VALUE_INIT;file = g_object_new (VIEWER_TYPE_FILE, NULL);g_value_init (&val, G_TYPE_UINT);
g_value_set_char (&val, 11);g_object_set_property (G_OBJECT (file), "zoom-level", &val);g_value_unset (&val);

之前,在介绍interface时,有个完整的例子使用过属性。Gobject tutorial 六-CSDN博客

现在我们介绍一下属性。

Properties

属性功能是由Gobject系统提供的。属性本身都是各种类型的数据,换句话说,对于单一的属性,它其实是一个类型的数据,可以是char *,也可以int,等等类型。属性是由对象实例维护,当然,这些对象实例都继承GObject。属性是按照名字来进行访问的,且一个实例中的对象,能够被其他实例访问。问题来了,这个“共享“是怎么做到的?我们后续会进行讨论。

在GObject中有很多方式可以设置和获取属性。

  • set方法有g_object_new、g_object_set, 后者是在应用中常用的方式。
  • get方法中应用最常用的方式是g_object_get.
GtkWindow *win;
win = g_object_new (GTK_TYPE_WINDOW, \"title", "Hello", \"default-width", 800, \"default-height", 600, NULL);

 另一个使用方式如下,

 

GtkWindow *win;
win = g_object_new (GTK_TYPE_WINDOW, NULL);
g_object_set (win, "title", "Good bye", NULL);

那么,如何获取呢,

GtkWindow *win;
char *title;
int width, height;win = g_object_new (GTK_TYPE_WINDOW, \"title", "Hello", \"default-width", 800, \"default-height", 600, NULL);
g_object_get (win, "title", &title, \"default-width", &width, \"default-height", &height, NULL);
g_print ("%s, %d, %d\n", title, width, height);
g_free (title);
 GParamSpec

说到属性,我们不得不对GParamSpec进行说明。GParamSpec是一个fundamental object, 它与GObject并无父子关系,它的作用是保存参数的信息,当然此处的参数也可以是属性。

我们看看它在属性注册过程中的作用。

/*gtk 4.8.2 demo3widget.c*/
static void
demo3_widget_class_init (Demo3WidgetClass *class)
{GObjectClass *object_class = G_OBJECT_CLASS (class);
......object_class->dispose = demo3_widget_dispose;object_class->set_property = demo3_widget_set_property;object_class->get_property = demo3_widget_get_property;......g_object_class_install_property (object_class, PROP_PAINTABLE,g_param_spec_object ("paintable", "Paintable", "Paintable",GDK_TYPE_PAINTABLE,G_PARAM_READWRITE));g_object_class_install_property (object_class, PROP_SCALE,g_param_spec_float ("scale", "Scale", "Scale",0.0, 10.0, 1.0,G_PARAM_READWRITE));......
}

注册 属性是通过函数g_object_class_install_property完成的,新类型在注册时,会实现自己的set_property/get_property函数,如上面的demo3_widget_set_property/demo3_widget_get_property。

g_object_class_install_property函数的声明如下,

void        g_object_class_install_property   (GObjectClass   *oclass,guint           property_id,GParamSpec     *pspec)

既然在注册属性过程中,GparamSpec是必须的,那么,就有必要对生成GParamSpec的函数进行说明,我们以上例中的g_param_spec_float为例,看看Glib的说明。

/*** g_param_spec_float:* @name: canonical name of the property specified* @nick: (nullable): nick name for the property specified* @blurb: (nullable): description of the property specified* @minimum: minimum value for the property specified* @maximum: maximum value for the property specified* @default_value: default value for the property specified* @flags: flags for the property specified** Creates a new #GParamSpecFloat instance specifying a %G_TYPE_FLOAT property.** See g_param_spec_internal() for details on property names.** Returns: (transfer full): a newly created parameter specification*/
GParamSpec*
g_param_spec_float (const gchar *name,const gchar *nick,const gchar *blurb,gfloat	 minimum,gfloat	 maximum,gfloat	 default_value,GParamFlags	 flags)
 Overriding set_property and get_property class methods

重写set_property 、get_property的过程如下:

 对于不同的对象实例,属性值是不同的,因此,属性值是存储在对象的实例中的。具体这是怎么做的呢。我们以上例中的demo3_widget_set_property为例进行说明。

static void
demo3_widget_set_property (GObject      *object,guint         prop_id,const GValue *value,GParamSpec   *pspec)
{Demo3Widget *self = DEMO3_WIDGET (object);switch (prop_id){case PROP_PAINTABLE:g_clear_object (&self->paintable);self->paintable = g_value_dup_object (value);gtk_widget_queue_resize (GTK_WIDGET (object));break;case PROP_SCALE:self->scale = g_value_get_float (value);gtk_widget_queue_resize (GTK_WIDGET (object));break;default:G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);break;}
}

可以看到需要设置的属性值value,保存在self->paintable、self->scale中。Demo3Widget的定义如下,

struct _Demo3Widget
{GtkWidget parent_instance;GdkPaintable *paintable;float scale;GtkWidget *menu;
};
Notify signal

还有一个重要的问题就是,当设置属性后,GObject会发出“notify"信号。如果你在程序中给信号链接了处理函数,你就能进行后续操作。notify信号在链接处理函数时,是有格式要求的。如下:

g_signal_connect (G_OBJECT (d1), "notify::value", G_CALLBACK (notify_cb), NULL);

上面示例中“notify::value”中的value需要替换成属性名。如果不设置value,那么,对象的任何属性被设置,回调函数都会执行。 注意,我们这里说,当属性被设置时,会发出notify 信号,那么,问题来了,我为属性设置相同的值,会不会有notify信号呢?答案是,会有的。那如果我想只在信号值发生改变时,才受到notify信号,该怎么办呢?这种情况下,首先我们在调用函数生成GParamSpec时,为函数设置属性G_PARAM_EXPLICIT_NOTIFY标记,具体来说,对于上面提到的函数g_param_spec_float为例,设置其最后的参数flag为G_PARAM_EXPLICIT_NOTIFY。其次需要在属性发生变化时,手动调用函数g_object_notify_by_pspec来发送notify 信号。

 

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

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

相关文章

怎么将几段音频合并在一起,试试这几个音频拼接小妙招

怎么将多个音频合并在一起呢?音频是我们日常工作生活中常见的文件,音频与我们息息相关,无论你是音乐爱好者,还是喜欢记录生活中的声音,都离不开音频。因此我们会遇到关于很多音频剪辑的难题,就像今天小编给…

usb摄像头应用编程

作者简介: 一个平凡而乐于分享的小比特,中南民族大学通信工程专业研究生在读,研究方向无线联邦学习 擅长领域:驱动开发,嵌入式软件开发,BSP开发 作者主页:一个平凡而乐于分享的小比特的个人主页…

auto/范围for/nullptr(C++)

文章目录 前言auto范围fornullptr总结 前言 auto auto主要用于类型推导,代替长类型 auto在c语言使用时修饰变量,表示这个变量是具有自动存储器的局部变量,但是在实际中几乎不会使用。 C11赋予了他新的含义,auto作为一个新的类型…

conda install xformers -c xformers/label/dev 的安装问题

在StableSR项目框架中,需要执行 conda install xformers -c xformers/label/dev 但是报错,错误显示,版本不匹配,如下所示: 我改用pip来安装,好像就不报错了: pip install xformers

javaWeb项目-ssm+vue企业台账管理平台功能介绍

本项目源码:javaweb项目ssm-vue企业台账管理平台源码-说明文档资源-CSDN文库 项目关键技术 开发工具:IDEA 、Eclipse 编程语言: Java 数据库: MySQL5.7 框架:ssm、Springboot 前端:Vue、ElementUI 关键技术:springboo…

vue项目——前端CryptoJS加密、解密

1、vue项目需要安装CryptoJS安装包 npm install crypto-js 2、在项目中引入CryptoJS import CryptoJS from crypto-js 3、使用,代码如下 // 此处key为16进制let key jiajiajiajiajiajiajiajia;console.log(密钥:, key);// key格式化处理key Crypt…

中国环保网元宇宙:开启绿色数字生活新篇章

在全球数字化浪潮和环境保护的双重推动下,"中国环保网元宇宙"应运而生,它不仅代表着技术的革新,更是环保意识在数字世界中的深刻体现。这一平台旨在通过沉浸式的虚拟现实技术,让公众更加直观地理解环保的重要性&#xf…

Baidu 搜狐面经

百度一面 1、Spring中有哪些常用注解? 2、如果一个服务入参是JsonString,出参也是jsonString,这个服务可能用到什么注解? 3、HSF的底层原理是否了解?序列化传输的协议是什么? 4、Postgrep Sql相比mysql…

【前端开发工具】VS Code安装和使用

文章目录 一、前言二、下载三、安装四、配置五、使用5.1 导入项目5.2 本地运行项目5.3 修改界面文案,验证效果5.4 添加日志打印5.5 代码调试5.6 代码提交到Git仓库 六、总结 一、前言 本文介绍一下在前端vue项目中,VS Code的安装和配置。 什么是VS Code…

【干货分享】25地学考研推免夏令营汇总表

​ 25考研学子们,考研准备要赶早。 小编给大家整合准备了25地信考研夏令营时间信息表,需要的宝子收藏起来。 ​ 话不多说,需要的小伙伴直接评论区留言 25地信考研择校信息表:

IT入门知识第一部分《IT基础知识》(1/10)

目录 IT入门知识第一部分《IT基础知识》(1/10) 1.引言 2.第一部分:IT基础知识 2.1 计算机硬件 CPU:计算机的心脏 内存:数据的临时居所 存储设备:数据的长期仓库 输入输出设备:与计算机的…

大模型日报2024-06-20

大模型日报 2024-06-20 大模型资讯 大模型产品 Genspark:AI智能搜索引擎 摘要: Genspark是一款AI智能引擎,专用AI代理生成无偏见的Sparkpages,提供高价值信息,节省用户时间。 AI标志动画生成工具 摘要: 使用AI技术轻松将静态标志变…

[Mysql] 的基础知识和sql 语句.教你速成(上)——逻辑清晰,涵盖完整

目录 前言 上篇的内容概况 下篇的内容概况 数据库的分类 关系型数据库 常见的关系型数据库系统 非关系型数据库 1. 键值对数据库(Key-Value Stores) 特点: 常见的键值对数据库: 2. 文档数据库(Document Store…

BUG: gradio RuntimeError: async generator raised StopAsyncIteration

BUG: gradio RuntimeError: async generator raised StopAsyncIteration 环境 gradio 4.20.0详情 在使用gradio编写大模型可视化demo的时候,大模型正常输出,但gradio弹出此错误。 经过排除,发现是返回方式的问题&…

【React】Axios请求头注入token

业务背景: Token作为用户的数据标识,在接口层面起到了接口权限控制的作用,也就是说后端有很多接口都需要通过查看当前请求头信息中是否含有token数据,来决定是否正常返回数据 // 添加请求拦截器 request.interceptors.request.use(config …

填坑-celery正常启动后能收到任务但不执行任务的解决办法

场景 Flask开发中用celery 6正常启动后能收到任务但不执行任务的解决办法,也没有错误提示…… INFO/MainProcess] Task app.add_together[ce406ed8-71b3-49e6-8556-f44bfe66549c] received [2024-06-20 19:38:10,632: INFO/SpawnPoolWorker-36] child process 2244…

MySQL中动态权限和角色管理权限的异同?

MySQL中的动态权限与角色管理权限是两个不同的概念,它们在权限管理方面各有特点和作用: 动态权限(Dynamic Privileges) 定义与特性:动态权限是在MySQL服务器运行时定义和注册的权限,与之相对的是静态权限&…

编译 CUDA 程序的基本知识和步骤

基本工具 NVCC(NVIDIA CUDA Compiler): nvcc 是 NVIDIA 提供的 CUDA 编译器,用于将 CUDA 源代码(.cu 文件)编译成可执行文件或库。它可以处理 CUDA 和主机代码(例如 C)的混合编译。nvcc 调用底层…

关于Threejs的使用二

Threejs之前是没有使用过的,由于项目需要最近一段时间一直在研究; 关于绘制字体: 有两种方案: 1.可以通过页面添加标签化元素进行插入到页面中: //創建元素const descriptionElement document.createElement(div);de…

求最小生成树的新算法

不管 prim 算法还是 kruskal 算法都基于 “当前可见最短边” 作贪心策略,但这并不适合分布式并行操作,比方说所有节点一起构建最小生成树,这些算法都显得同步开销过大,甚至导出错误的结果。 最近研究并构建最大流多路径传输协议的…