Gobject tutorial 八

news2024/7/15 19:57:26

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函数对对象属性进行操作。首先我们先举例说明属性如何定义的,以及在应用程序中如何使用。

首先,看一下定义。

// Implementation

typedef 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);
}

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

// Use

ViewerFile *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.coloradmin.cn/o/1844140.html

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

相关文章

基于JSP的个人交友网站系统

开头语: 你好,我是计算机学长猫哥。在这个信息时代,个人交友网站为我们提供了一个便捷的社交平台。如果你对本系统感兴趣或有更多需求,欢迎联系我。 开发语言:Java 数据库:MySQL 技术:JSP技…

多线程(Lock锁,死锁,等待唤醒机制,阻塞队列,线程池)

Lock锁 虽然我们可以理解同步代码块和同步方法的锁对象问题但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操…

网络编程(七)网络超时检测

文章目录 一、概念二、实现方式(一) 使用select实现超时检测1. select函数补充说明:2. 使用示例3. 输出结果 (二) 使用setsockopt函数1. 函数定义2. 获取发送缓冲区和接收缓冲区的大小3. 端口复用4. 设置超时时间 &…

每日复盘-202406020

今日关注: 20240620 六日涨幅最大: ------1--------300462--------- 华铭智能 五日涨幅最大: ------1--------300462--------- 华铭智能 四日涨幅最大: ------1--------300462--------- 华铭智能 三日涨幅最大: ------1--------300462--------- 华铭智能 二日涨幅最…

华为数通企业面试笔试实验题

1. 笔试题 1.1 实验拓扑 1.2 实验要求 公司A为小型销售公司,需要实现基本上网功能,蓝色部分为外网线,提供DHCP服务 DnsServer:114.114.114.114 帮助网管排查某一台计算机在某一台交换机的某个端口 2. 操作步骤 配置路由器相关的LAN侧接口IP地址 配置DHCP项,要求有PC1与PC2…

关于椭圆的方程(有Python画的动图)

关于椭圆的方程(有Python画的动图) flyfish 几何定义 椭圆是平面上所有到两个固定点(焦点)的距离之和为常数的点的集合。这两个固定点叫做焦点。 解析几何描述 设椭圆的两个焦点为 F 1 F_1 F1​ 和 F 2 F_2 F2​&#xff…

Redis学习|Redis主从复制、Redis哨兵模式、缓存穿透、缓存击穿、缓存雪崩概念和相应解决方法

Redis主从复制 主从复制,是指将一台Redis服务器的数据,复制到其他的Redis服务器。前者称为主节点(master/leader),后者称为从节点(slave/follower);数据的复制是单向的,只能由主节点到从节点。Master以写为主,Slave 以…

深度解读:如何运用600+标签精准构建企业画像、快速筛选高价值企业?

启信慧眼-启信宝企业版 近年来,信息安全越来越得到企业与公众的重视,数据库作为数据基础设施的关键部件,是保障关键信息基础设施安全的重要前提。数据库不仅为企业提供了数据存储和管理的基础设施,还通过数据分析、优化和扩展等功…

php反序列化的一些知识

问题 <?php $raw O:1:"A":1:{s:1:"a";s:1:"b";}; echo serialize(unserialize($raw)); //O:1:"A":1:{s:1:"a";s:1:"b";}?> php反序列化的时按理说找不到A这个类&#xff0c;但是他没有报错&#xff0c;…

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

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

Django使用django-apscheduler实现定时任务

定时任务可以在后台定时执行指定的代码&#xff0c;避免了很多人为操作。下面是在Django项目中如何使用定时任务的具体操作流程。 我在这里使用的 django-apscheduler库来实现定时任务。 一、安装 django-apscheduler pip install django-apscheduler二、在项目的setting.py…

vivado TIMING_PATH

描述 定时路径由设计元素之间的连接来定义。在数字中 在设计中&#xff0c;时序路径是由一对受其控制的顺序元件形成的 或者通过两个不同的时钟来启动和捕获信号。 在典型的定时路径中&#xff0c;数据在一个内的两个连续单元之间传输 时钟周期。例如&#xff0c;启动边缘发生在…

‘xxxx‘ is a program, not an importable package

程序出现报错&#xff1a;‘xxxx’ is a program, not an importable package&#xff1a;“xxxx”是一个程序&#xff0c;而不是一个可导入的包 当程序出现这种报错提示的时候&#xff0c;首先第一点要做的就是需要使用到的某个方法/函数/结构体等所在的.go文件上面的packag…

音视频开发—RTMP推流详解

RTMP&#xff08;Real-Time Messaging Protocol&#xff0c;实时消息传输协议&#xff09;是一种用于音视频和数据在互联网进行实时传输的协议&#xff0c;最初由Macromedia&#xff08;现Adobe&#xff09;开发&#xff0c;用于与Adobe Flash Player进行通信。RTMP被广泛用于流…

成都晨持绪:新手开抖音网店什么品好做

在如今这个互联网高速发展的时代&#xff0c;抖音作为一款流行的短视频平台&#xff0c;已经成为许多创业者开展线上业务的首选之地。对于刚入行的新手来说&#xff0c;开设一个抖音网店既是一个机遇也是挑战&#xff0c;选择什么样的商品销售成为成功的关键因素之一。 首要的是…

数据库异常恢复2-备份文件恢复(快速恢复的手动启动方式)

(四) 备份文件备份恢复的概念 本次所说的数据恢复有异于数据的导入导出 1. 备份工具 gbase8s数据库提供了两种工具进行完成系统物理备份、逻辑日志备份和系统恢复&#xff1a;ontape和onbar ontape&#xff1a;提供了基本的系统物理备份、日志备份和恢复能力&#xff0c;其…

java实现分类下拉树,点击时对应搜索---后端逻辑

一直想做分类下拉&#xff0c;然后选择后搜索的页面&#xff0c;正好做项目有了明确的需求&#xff0c;查找后发现el-tree的构件可满足需求&#xff0c;数据要求为&#xff1a;{ id:1, label:name, childer:[……] }形式的&#xff0c;于是乎&#xff0c;开搞&#xff01; 一…

QT自定义标题栏窗口其二:实现拖动及可拉伸效果 + 顶部全屏/侧边半屏

1、效果 2、核心代码 #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent<

管理咨询公司的五个招聘秘密

在管理咨询中&#xff0c;人是业务&#xff1b;客户支付数百万美元&#xff0c;要求管理顾问确认问题&#xff0c;并推荐解决方案。由于收入和合规性受到威胁&#xff0c;招聘错误的成本可能非常昂贵&#xff0c;一些公司更倾向于谨慎而不是效率。然而&#xff0c;在当今竞争激…

掌握大型语言模型的 7 个基本步骤

介绍 LLMs 正在改变我们今天与科技互动的方式。这些人工智能程序能够理解和模仿人类语言。它们可以应用于数据分析、客户服务、内容创作和其他领域。但对于新手来说&#xff0c;了解如何使用它们似乎很有挑战性。本文将引导读者了解掌握大型语言模型的 7 个基本步骤。 本文还…