Qt deleteLater 延遲刪除原理

deleteLater 調用

事件發送

void QObject::deleteLater()
{QCoreApplication::postEvent(this, new QDeferredDeleteEvent());
}
  • 首先該對象繼承QObject
  • 調用deleteLater, 內部會發送刪除事件QCoreApplication::postEvent(this, new QDeferredDeleteEvent()) 到事件循環的隊列中
  • 最終事件循環會調用 reciver->event(Qevent)

事件接收

bool QObject::event(QEvent *e)
{switch (e->type()) {case QEvent::Timer:timerEvent((QTimerEvent*)e);break;case QEvent::ChildAdded:case QEvent::ChildPolished:case QEvent::ChildRemoved:childEvent((QChildEvent*)e);break;case QEvent::DeferredDelete:qDeleteInEventHandler(this);break;...return true;
}void qDeleteInEventHandler(QObject *o)
{delete o;
}
  • event(QEvent *e) 通過事件類型判斷,是否為延遲刪除類型:QEvent::DeferredDelete
  • 如果是延遲刪除類型,則會調用qDeleteInEventHandler

事件發送實現

設置刪除事件的 looplevel 和 eventLevel

void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority)
{Q_TRACE_SCOPE(QCoreApplication_postEvent, receiver, event, event->type());if (receiver == nullptr) {qWarning("QCoreApplication::postEvent: Unexpected null receiver");delete event;return;}auto locker = QCoreApplicationPrivate::lockThreadPostEventList(receiver);if (!locker.threadData) {// posting during destruction? just delete the event to prevent a leakdelete event;return;}QThreadData *data = locker.threadData;// if this is one of the compressible events, do compressionif (receiver->d_func()->postedEvents&& self && self->compressEvent(event, receiver, &data->postEventList)) {Q_TRACE(QCoreApplication_postEvent_event_compressed, receiver, event);return;}if (event->type() == QEvent::DeferredDelete)receiver->d_ptr->deleteLaterCalled = true;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;}// delete the event on exceptions to protect against memory leaks till the event is// properly owned in the postEventListQScopedPointer<QEvent> eventDeleter(event);Q_TRACE(QCoreApplication_postEvent_event_posted, receiver, event, event->type());data->postEventList.addEvent(QPostEvent(receiver, event, priority));eventDeleter.take();event->posted = true;++receiver->d_func()->postedEvents;data->canWait = false;locker.unlock();QAbstractEventDispatcher* dispatcher = data->eventDispatcher.loadAcquire();if (dispatcher)dispatcher->wakeUp();
}

通過looplevel 和 eventLevel 判斷 確認是是否執行延遲刪除事件

 const bool allowDeferredDelete =(eventLevel > loopLevel|| (!eventLevel && loopLevel > 0)|| (event_type == QEvent::DeferredDelete&& eventLevel == loopLevel));if (!allowDeferredDelete) {// cannot send deferred deleteif (!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 lostdata->postEventList.addEvent(pe_copy);}continue;}
  • 如果符合條件 調用 QCoreApplication::sendEvent(r, e);
  • 不符合條件 制空當前隊列指針const_cast<QPostEvent &>(pe).event = nullptr;,將指針副本插入到隊列尾部 continue 跳過``(完成一次延遲刪除的機會)

最總調用event 事件:

bool QCoreApplicationPrivate::notify_helper(QObject *receiver, QEvent * event)
{// Note: when adjusting the tracepoints in here// consider adjusting QApplicationPrivate::notify_helper too.Q_TRACE(QCoreApplication_notify_entry, receiver, event, event->type());bool consumed = false;bool filtered = false;Q_TRACE_EXIT(QCoreApplication_notify_exit, consumed, filtered);// send to all application event filters (only does anything in the main thread)if (QCoreApplication::self&& receiver->d_func()->threadData.loadRelaxed()->thread.loadAcquire() == mainThread()&& QCoreApplication::self->d_func()->sendThroughApplicationEventFilters(receiver, event)) {filtered = true;return filtered;}// send to all receiver event filtersif (sendThroughObjectEventFilters(receiver, event)) {filtered = true;return filtered;}// deliver the eventconsumed = receiver->event(event);return consumed;
}

延遲刪除的核心(個人理解)

  • 1: 就是通過記錄looplevel 和 eventlevel 然后在條件比較兩者
  • 2:eventLevel 是每個事件中的一個快照相當于curentlevel, 他記錄的是當前looplevel, 而looplevel 是不斷變換的。相當于looplevel 是一個動態的全局或者靜態的變量, eventLevel 是每個事件中的一個屬性

場景1:主循環中的延遲刪除

// loopLevel = 0 (初始)
QCoreApplication::exec(); // loopLevel → 1QObject* obj = new QObject;
obj->deleteLater(); // eventLevel = 1// 事件處理時:
//   eventLevel=1, loopLevel=1 → 條件3滿足 → 立即刪除

場景2:嵌套循環中的跨級刪除

// 主循環中 (loopLevel=1)
QObject* outerObj = new QObject;
outerObj->deleteLater(); // eventLevel=1QEventLoop nestedLoop; 
// 進入嵌套循環 (loopLevel=2)// 處理outerObj的延遲刪除事件:
//   eventLevel=1, loopLevel=2
//   1 > 2? false
//   !1 && 2>0? false
//   事件非DeferredDelete → 不滿足條件 → 跳過刪除

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/917266.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/917266.shtml
英文地址,請注明出處:http://en.pswp.cn/news/917266.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

TypeScript SDK 升級:通過 Upload Relay 賦能更多應用

自 3 月主網上線以來&#xff0c;Walrus 開發者社區持續展現出強勁的發展勢頭&#xff1a; 當前 Walrus 已存儲超 758 TB 數據&#xff0c;為數百個項目提供支持。在 2025 年 6 月舉辦的 Sui Overflow 黑客松上&#xff0c;Walrus 成為最受歡迎的數據層。該賽事共收到 599 個項…

C#線程同步(二)鎖

目錄 1.lock 2.Monitor 3.鎖的其它要注意的問題 3.1同步對象的選擇 3.2什么時候該上鎖 3.3鎖和原子性 3.4嵌套鎖 3.5 死鎖 3.6 性能 4.Mutex 5.Semaphore 1.lock 讓我們先看一段代碼&#xff1a; class ThreadUnsafe {static int _val1 1, _val2 1;static void G…

鴻蒙智能居家養老系統構思(續二)—— 適老化烹飪中心詳細構思

一、背景在“寫給華為鴻蒙智家 —— 智能居家養老系統構思”一文中&#xff0c;結合對居家養老的理解及個人體驗&#xff0c;提出了基于鴻蒙OS實現居家養老系統的粗略構思。其中包含“吃得好”。當老人到了不能隨性外出活動、只能在家消耗時光時&#xff0c;除了一些看看電視、…

高斯透鏡公式(調整鏡頭與感光元件之間的距離時,使得不同距離的物體在感光元件上形成清晰的影像)

當使用定焦鏡頭時&#xff0c;仍然可以調整鏡頭與感光元件&#xff08;或膠片&#xff09;之間的距離時&#xff0c;使得不同距離的物體在感光元件上形成清晰的影像。對此可以用高斯透鏡公式進行解釋&#xff1a; 一、透鏡成像的基本原理 在光學中&#xff0c;一個基本的公式是…

預過濾環境光貼圖制作教程:第三階段 - GGX 分布預過濾

核心目標 GGX 分布是 PBR 中模擬粗糙表面高光反射的主流模型,其核心是通過統計分布描述微表面的朝向概率。本階段的目標是: 基于第一階段生成的環境圖集,預計算 6 個級別的 GGX 過濾結果(對應不同粗糙度); 使用蒙特卡洛采樣(Monte Carlo Sampling)加速 GGX 卷積計算;…

Spring框架與AutoCAD結合應用

什么是AutoCAD? AutoCAD簡介 AutoCAD是由美國Autodesk公司開發的計算機輔助設計(CAD)軟件,廣泛應用于建筑、工程、制造、產品設計等領域。它支持2D繪圖和3D建模,提供精確的圖形工具和自動化功能,幫助用戶高效創建技術圖紙和模型。 主要功能 2D繪圖:提供直線、圓弧、多…

Java 學習筆記:常用類、String 與日期時間處理

作為一名名 Java 初學者&#xff0c;最近在學習過程中整理了一些關于常用類、String 類以及日期時間處理的知識點。這些內容是 Java 基礎中的重點&#xff0c;也是日常編程練習中頻繁用到的工具&#xff0c;掌握它們能讓我們在寫代碼時更加得心應手。今天把這些筆記分享出來&am…

Android常用的adb和logcat命令

ADB ADB&#xff0c;即 Android Debug Bridge 是一種允許模擬器或已連接的 Android 設備進行通信的命令行工具&#xff0c;它可為各種設備操作提供便利&#xff0c;如安裝和調試應用&#xff0c;并提供對 Unix shell&#xff08;可用來在模擬器或連接的設備上運行各種命令&…

重學JS-001 --- JavaScript算法與數據結構(一)JavaScript 基礎知識

文章目錄 變量 變量命名規則 變量命名 let vs const 變量使用范圍 賦值 = 控制臺輸出 運算符 ++ -- == === !== 注釋 轉義字符 數據類型 7種 原始數據類型 1. string?? 2. number?? 3. ??boolean?? 4. null?? 5. undefined?? 6. ??symbol??(ES6 新增) 7. big…

MySQL數據閃回工具my2sql的使用

場景&#xff1a; 當你或者其它人員誤操作數據庫不小心刪除或者更新了一批數據&#xff0c;但是是當時又沒事先備份時&#xff0c;你可以 用這個 my2sql工具快速幫你找回數據。就是如此的絲滑。但是要注意的是只限于dml語句&#xff0c;所以我們在操作數據庫前必需先備份哦&…

9.1無法恢復的錯誤與 panic!

無法恢復的錯誤與 panic! 有時你的代碼中會發生嚴重問題&#xff0c;而你無能為力。在這些情況下&#xff0c;Rust 提供了 panic! 宏。實際上&#xff0c;有兩種方式會導致 panic&#xff1a;一種是執行某個操作使代碼產生 panic&#xff08;例如訪問數組越界&#xff09;&…

分享低功耗單火線開關語音識別方案

在眾多老舊建筑和常規家居環境里&#xff0c;單火線布線是主流方式。單火線語音識別芯片方案通過研發和應用特殊的單火線語音識別芯片&#xff0c;實現設備在單火線供電條件下穩定運行&#xff0c;并精準識別語音指令&#xff0c;為智能家居、智能照明等領域帶來便捷的語音控制…

如何在Windows操作系統上通過conda 安裝 MDAnalysis

MDAnalysis 是一個開源的 Python 庫,旨在提供一個高效且靈活的方式來分析和處理分子動力學(MD)模擬數據。它可以從不同的文件格式中讀取模擬軌跡和結構數據,進行復雜的數據處理和分析,廣泛應用于生物物理學、化學、材料科學等領域。 一、創建虛擬環境 為了能夠順利安裝,減…

實用PDF演示解決方案

它打破了傳統閱 讀模式&#xff0c;讓PDF文檔也能像PPT一樣流暢播放&#xff0c;特別適合匯報、講解等展示場景。它是綠色單文件版&#xff0c;無需安裝&#xff0c;雙擊紅色圖標即點即用。運行后第一件事&#xff0c;建議把界面語言切換成中文&#xff0c;操作更順手。導入PDF…

VS Code中如何關閉Github Copilot

點擊頂部搜索欄后面的Copilot圖標&#xff0c;在下拉菜單中選擇Hide Copilot。在彈出的提示框中&#xff0c;點擊Hide Copilot按鈕就可以了。

MySQL學習從零開始--第六部分

Binlog是什么&#xff1f;有哪幾種格式&#xff1f;推薦使用哪種&#xff0c;為什么 Binlog是什么 Binlog二進制日志是MySQL Server層記錄所有更改數據庫內容的操作日志的二進制文件&#xff0c;如操作UPDATE,DELETE,INSERTBinlog不記錄SELECT&#xff0c;SHOW等查詢操作使主從…

走進computed,了解computed的前世今生

computed&#xff08;計算屬性&#xff09;并不是vue獨創的&#xff0c;而是源自計算機科學和響應式編程的長期發展 計算理論的奠基&#xff1a; 函數式編程的純函數思想&#xff1a;計算屬性的核心特征&#xff08;無副作用、依賴輸入確定輸出&#xff09;直接來源于函數式編程…

Java 23 新特性解析與代碼示例

Java 23 新特性解析與代碼示例 文章目錄Java 23 新特性解析與代碼示例1. 引言2. 正式特性2.1. Markdown文檔注釋 (JEP 467)2.2. 廢棄sun.misc.Unsafe的內存訪問方法以移除 (JEP 471)2.3. ZGC&#xff1a;默認啟用代際模式 (JEP 474)3. 預覽特性3.1. 原始類型在模式、instanceof…

spring boot + mybatis + mysql 只有一個實體類的demo

使用MyBatis進行數據庫操作&#xff0c;配置簡單。主要演示了mybatis可以不用只使用方法名來對應mapper.java和mapper.xml。 目錄結構 pom.xml src/ ├── main/ │ ├── java/ │ │ └── com/ │ │ └── springbootjdbcweb/ │ │ └── …

iRemovalPro完美繞iCloud插卡打電話,A12+支持iOS 18.1.1

iRemovalPro 專業工具全解析與操作指南 &#xff08;支持iOS 14.0 - 16.6.1&#xff0c;A7-A15芯片設備&#xff09; &#x1f449;下載地址見文末 iRemoval Pro iRemoval 專業版是一款來自外國安全研究員的工具&#xff0c;用來幫助一些人因為忘記自己的ID或者密碼&#xff0c…