- Java锁的逻辑(结合对象头和ObjectMonitor)
- 还在用饼状图?来瞧瞧这些炫酷的百分比可视化新图形(附代码实现)⛵
- 自动注册实体类到EntityFrameworkCore上下文,并适配ABP及ABPVNext
- 基于Sklearn机器学习代码实战
个人经验总结,如有错误或遗漏,欢迎各位大佬指正 🥳 。
在本篇文章中,我们将深入分析源码,探讨 deleteLater 的原理.
deleteLater 是Qt框架提供的一个重要函数,用于在事件循环中延迟删除对象.
在软件开发中,延迟删除对象的概念是非常重要的,特别是当对象不再被需要,但由于某些原因(比如对象正在被访问、事件循环中仍有关于该对象的事件等),不能立即删除。 deleteLater 函数提供了一种机制,可以将对象的删除操作延迟执行,从而避免了可能的悬空指针和资源泄漏问题.
在本文中,我们将深入研究源码,揭示 deleteLater 函数的工作原理。我们将分析 QCoreApplication 的 postEvent 函数以及 QObject 的 event 函数,并解释它们如何协同工作来实现延迟删除的效果.
通过深入分析源码并理解 deleteLater 的内部工作原理,我们将更好地了解它在Qt框架中的作用和优势.
根据官方文档中的解释:
Schedules this object for deletion. 。
The object will be deleted when control returns to the event loop. If the event loop is not running when this function is called (e.g. deleteLater() is called on an object before QCoreApplication::exec()), the object will be deleted once the event loop is started. If deleteLater() is called after the main event loop has stopped, the object will not be deleted. Since Qt 4.8, if deleteLater() is called on an object that lives in a thread with no running event loop, the object will be destroyed when the thread finishes. 。
Note that entering and leaving a new event loop (e.g., by opening a modal dialog) will not perform the deferred deletion; for the object to be deleted, the control must return to the event loop from which deleteLater() was called. This does not apply to objects deleted while a previous, nested event loop was still running: the Qt event loop will delete those objects as soon as the new nested event loop starts. 。
计划删除一个对象.
当控制返回到事件循环时,对象将被删除 。如果在调用此函数时事件循环没有运行(例如,在QCoreApplication::exec()之前在对象上调用deleteLater()),则一旦事件循环启动,该对象将被删除。如果在主事件循环停止后调用deleteLater(),则不会删除对象。自Qt 4.8以来,如果deleteLater()在没有运行事件循环的线程中的对象上调用,则该对象将在线程结束时被销毁.
注意,进入和离开一个新的事件循环(例如,通过打开一个模态对话框)不会执行延迟删除;对于要删除的对象,必须返回到从中调用deleteLater()的事件循环。这不适用于在以前的嵌套事件循环仍在运行时删除的对象:Qt事件循环将在新的嵌套事件循环开始时删除这些对象.
🙋♂️那为什么需要把这样一个删除动作丢到事件循环去呢?
因为也许在执行删除某个QObject对象的操作时,事件队列中仍然有关于这个QObject对象的事件,如果直接删除该对象,就会导致后面调用这个对象时,引起程序的崩溃。也就是解决悬空指针的问题.
首先,我们先看 deleteLater 做了些什么操作:
void QObject::deleteLater()
{
QCoreApplication::postEvent(this, new QDeferredDeleteEvent());
}
可以看到, deleteLater 函数中,仅仅只是往事件队列中添加了一个 DeferedDelete 事件.
其次,我们再看QObject的事件处理函数—— event 。
bool QObject::event(QEvent *e)
{
switch (e->type()) {
// ...
case QEvent::DeferredDelete:
qDeleteInEventHandler(this);
break;
// ...
}
void qDeleteInEventHandler(QObject *o)
{
delete o;
}
从上面,我们可以看到, deleteLater 发出来的事件,最后处理的时候,就是简简单单的调用delete进行析构。也就能印证deleteLater的作用:
计划删除一个对象。 当控制返回到事件循环时,对象将被删除 .
但作为一位技术人,看到官方文档中,对这个函数有一段这么长的描述,总觉得事情是不是有点太简单了,难道是我打开的方式不对?抱着这个想法,我又看了一遍源码,也算是找到了一些另外的处理.
这次,我从 deleteLater 中发送事件( postEvent )下手,看是不是这个里面有着特殊处理:
void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority)
{
// ...
if (event->type() == QEvent::DeferredDelete && data == QThreadData::current()) {
// remember the current running eventloop for DeferredDelete
// events posted in the receiver's thread.
// Events sent by non-Qt event handlers (such as glib) may not
// have the scopeLevel set correctly. The scope level makes sure that
// code like this:
// foo->deleteLater();
// qApp->processEvents(); // without passing QEvent::DeferredDelete
// will not cause "foo" to be deleted before returning to the event loop.
// If the scope level is 0 while loopLevel != 0, we are called from a
// non-conformant code path, and our best guess is that the scope level
// should be 1. (Loop level 0 is special: it means that no event loops
// are running.)
int loopLevel = data->loopLevel;
int scopeLevel = data->scopeLevel;
if (scopeLevel == 0 && loopLevel != 0)
scopeLevel = 1;
static_cast<QDeferredDeleteEvent *>(event)->level = loopLevel + scopeLevel;
}
// ...
}
在这里面,把这个 DeferedDelete 事件中的循环级别设置了一下,以便待会进行事件发送的时候,能够进行当前事件是否要处理的判断.
void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type,
QThreadData *data)
{
// ...
if (pe.event->type() == QEvent::DeferredDelete) {
// DeferredDelete events are sent either
// 1) when the event loop that posted the event has returned; or
// 2) if explicitly requested (with QEvent::DeferredDelete) for
// events posted by the current event loop; or
// 3) if the event was posted before the outermost event loop.
int eventLevel = static_cast<QDeferredDeleteEvent *>(pe.event)->loopLevel();
int loopLevel = data->loopLevel + data->scopeLevel;
const bool allowDeferredDelete =
(eventLevel > loopLevel
|| (!eventLevel && loopLevel > 0)
|| (event_type == QEvent::DeferredDelete
&& eventLevel == loopLevel));
if (!allowDeferredDelete) {
// cannot send deferred delete
if (!event_type && !receiver) {
// we must copy it first; we want to re-post the event
// with the event pointer intact, but we can't delay
// nulling the event ptr until after re-posting, as
// addEvent may invalidate pe.
QPostEvent pe_copy = pe;
// null out the event so if sendPostedEvents recurses, it
// will ignore this one, as it's been re-posted.
const_cast<QPostEvent &>(pe).event = nullptr;
// re-post the copied event so it isn't lost
data->postEventList.addEvent(pe_copy);
}
continue;
}
}
// ...
}
上面代码中,我们可以看到判断当前是否允许处理 DeferredDelete 事件时,有三个条件,也就是上面注释所说的:
const bool allowDeferredDelete =
(eventLevel > loopLevel
|| (!eventLevel && loopLevel > 0)
|| (event_type == QEvent::DeferredDelete
&& eventLevel == loopLevel));
如果 DeferredDelete 不能在当前的循环中被处理,那么将事件重新封装,重新添加到线程的事件列表中,等待能够处理该事件的事件循环去处理.
通过上面的分析,调用 deleteLater 会延迟删除一个QObject对象,并最终妥善的删除。但是,在我们使用的过程中,仍然是要注意,将原指针置 nullptr 的,不然就会导致野指针了.
那么, 我们应该在什么时候去进行置空处理呢 ?
Qt官方文档中有做解释:
[signal] void QObject::destroyed(QObject *obj = nullptr) 。
This signal is emitted immediately before the object obj is destroyed, after any instances of QPointer have been notified, and cannot be blocked. All the objects's children are destroyed immediately after this signal is emitted 。
在一个QObject对象析构时,会发射一个信号—— destroyed 。使用方法如下:
QObject* obj = new QObject();
connect(obj, &QObject::destroyed, [&] () { obj = nullptr; });
obj->deleteLater();
💕 创作不易,如果本篇博客对您有帮助,麻烦点赞、收藏、关注支持一下😉 。
最后此篇关于Qt源码阅读(五)-deleteLater的文章就讲到这里了,如果你想了解更多关于Qt源码阅读(五)-deleteLater的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
我正在尝试读取一个大型日志文件,该文件已使用不同的分隔符(遗留更改)进行了解析。 此代码有效 import os, subprocess, time, re import pandas as pd f
我试图理解在 Linux 下以 Turbo 模式(特别是 fpc -Mtp -vw)编译的 Free Pascal 中看到的有点神奇的行为。代码来自 Jack Crenshaw 的“让我们构建一个编译
我有一个具有以下结构的 txt 文件: NAME DATA1 DATA2 a 10 1,2,3 b 6 8,9 c 2
我试图理解在 Linux 下以 Turbo 模式(特别是 fpc -Mtp -vw)编译的 Free Pascal 中看到的有点神奇的行为。代码来自 Jack Crenshaw 的“让我们构建一个编译
public class Bug1 { private String s; public void Bug1(){ s = "hello"; } public Stri
我们有这样一种情况,我们的应用程序需要处理一系列文件,而不是同步执行此功能,我们希望采用多线程将工作负载分配给不同的线程。 每一项工作是: 1.以只读方式打开文件 2.处理文件中的数据 3.将处理后的
我正在尝试读取 .php 文件并替换十六进制字符。php文件格式如下: 问题是它弄乱了转义字符 (\") 到目前为止我的代码: while(i=48 && str[i+2]=97 && str[i+
我正在用 C# 开发一个程序,我需要一些帮助。我正在尝试创建一个数组或项目列表,显示在某个网站上。我想要做的是阅读 anchor 文本,它是 href。例如,这是 HTML:
我有一个偏好设置,它控制我的应用程序是否在用户单击按钮时播放声音(这种情况经常发生,想想计算器)。每次用户单击按钮时,都会调用以下方法: private void playButtonClickSou
我正在尝试在我的标签末尾创建一个阅读更多按钮。我希望它默认显示 3 行。我正在用 swift 而不是 objective c 编写代码。只有当用户点击标签的阅读更多部分时,标签才会展开。它的外观和工作
当您获得第三方库(c、c++)、开源(LGPL 说)但没有很好的文档时,了解它以便能够集成到您的应用程序中的最佳方法是什么? 该库通常有一些示例程序,我最终使用 gdb 浏览了代码。还有其他建议/最佳
同时从 2 个或更多不同线程对同一个文件描述符使用 pread 是否有问题? 最佳答案 pread 本身是线程安全的,因为它不在 list of unsafe functions 上.所以调用它是安全
当您使用命令 pd.read_csv 读取 csv 时,如何跳过连续包含特定值的行?如果在第 50、55 行,第一列的值为 100,那么我想在读取 csv 文件时跳过这些行。我如何将这些命令放入像 p
我迫切需要在 C# 中使用 T4 生成 HTML 输出。 我正在使用 Runtime-T4-Files 并选择“TextTemplatingFilePreprocessor”而不是“TextTempl
今年夏天我在实习期间一直在学习 ERP 应用程序。由于我是一名即将毕业的程序员,我希望有一个可靠的软件分支可以帮助我完成工作,直到我确定下一步该做什么(直到我对大局有一个很好的了解)。到现在为止,我刚
将包含列(例如“a”、“b”)的数据帧保存为 parquet,然后在稍后的时间点读取 parquet 不会提供相同的列顺序(可能是“b”、“a”fe)文件保存为。 不幸的是,我无法弄清楚订单是如何受到
我正在开发一个使用谷歌表格作为数据库的应用程序,但我不知道如何让 Swift 从谷歌表格中读取。我浏览了 API 网站和一些问题,但刚开始我需要一些帮助。到目前为止,我有; 私有(private)让范
我打算阅读swing concept,如果值得一读,请推荐一些学习 Material 最佳答案 自 AWT 崩溃以来,Java 的 GUI 工具包太多了。即使是 Swing 也被评论家严重低估,但他们
我已经使用 J 几个月了,我发现阅读不熟悉的代码(例如,不是我自己写的)是该语言最具挑战性的方面之一,尤其是在默认情况下。过了一会儿,我想出了这个策略: 1)将代码段复制到word文档中 2)从(1)
很难说出这里问的是什么。这个问题是含糊的、模糊的、不完整的、过于宽泛的或修辞性的,无法以目前的形式得到合理的回答。如需帮助澄清此问题以便重新打开它,visit the help center 。 已关
我是一名优秀的程序员,十分优秀!