【iOS】retain/release底層實現原理

文章目錄

    • 前言
    • 前情知識
    • retain和release的實現原理(MRC手動管理)
      • retain(MRC手動管理)
        • retain源碼
        • 內聯函數rootRetain源碼
        • 相關的sidetable_tryRetain()方法
        • retain底層工作流程總結
      • release
        • release源碼
        • 內聯函數rootRelease源碼
    • 小結

前言

??在前面OC的學習中,我們了解到了OC中的關鍵字,今天我們來具體解析一下strong、copy、retain、release的實現原理,他們都是內存管理的核心機制,其底層實現深度依賴引用計數(Retain Count)運行時(Runtime)的 SideTable機制

前情知識

??在了解其底層原理之前,我們先來回顧一下引用計數的相關概念。

??OC內存管理的核心是引用計數(ARC 下由編譯器自動管理,MRC 下手動操作)。每個對象有一個隱式的引用計數retainCount),表示當前有多少個“擁有者”持有該對象:

  • 引用計數 +1:對象被“持有”(如 retainstrong賦值)。
  • 引用計數 -1:對象被“釋放”(如 releasestrong變量超出作用域)。
  • 引用計數為 0:對象被銷毀(調用 dealloc),內存被回收。

??OC對象的內存生命周期由引用計數控制。每個對象的內存頭部(objc_object結構體)都存儲了一個isa指針,以及一個隱藏的引用計數字段(retainCount)。當引用計數變為0時,對象被銷毀(調用dealloc),內存被回收。

引用計數的存儲方式

  • 小對象優化:對于小對象(如NSNumber、NSNull等小尺寸對象)

NSNumber:基本數據類型(如 int、float、BOOL等)的對象化包裝

NSNull:空值的對象化表示。NSNull是一個特殊的類,用于表示“空值”(類似其他語言的 null或 None)

  • 大對象:對于大對象(如NSObject子類、NSArray等),引用計數存儲在全局的SideTable中。SideTable是一個哈希表,通過對象的地址作為鍵,映射到包含引用計數和弱引用表的條目(side_table_t)。

retain和release的實現原理(MRC手動管理)

??在MRC模式下,retain和release是手動管理對象生命周期的核心方法,直接操作引用計數。

retain(MRC手動管理)

retain源碼

??retain的作用是增加對象的引用計數,確保對象在被持有期間不會被釋放。關于retain這部分的源碼,在objc4_906_main中如下:

inline id 
objc_object::retain()
{//確保對象不是小對象,如果是,就直接返回自身,因為小對象的值直接編碼在指針地址中,無需堆分配,不用引用計數ASSERT(!isTaggedPointer());return rootRetain(false, RRVariant::FastOrMsgSend);
}

在retain內聯的rootRetain函數前面,還有一個其的無參數版本:

ALWAYS_INLINE id 
objc_object::rootRetain()
{return rootRetain(false, RRVariant::Fast);
}

這段無參數的rootRetain方法版本,核心作用是通過內聯調用簡化代碼,并為特定場景(如內部優化路徑)提供更高效的調用方式,快速觸發對象的引用計數增加功能操作。

內聯函數rootRetain源碼

進入rootRetain后,源碼如下:

ALWAYS_INLINE id
//rootRetain是retain操作的核心實現,負責處理引用計數的原子性增加、多線程安全、內聯引用計數與側表的轉錄等
//參數:tryRetain為false,表示這是一個普通的保留操作,而非嘗試保留(嘗試保留通常用于鎖機制中,避免阻塞)
//     RRVariant::FastOrMsgSend是一個枚舉值,指示當前調用路徑是快速路徑或模擬objc_msgSend發送retain消息的場景
objc_object::rootRetain(bool tryRetain, objc_object::RRVariant variant)
{//檢查對象是否為小對象(如 int、BOOL、小 NSNumber等)。小對象的值直接編碼在指針地址中(無需堆分配),無需引用計數管理if (slowpath(isTaggedPointer())) return (id)this;bool sideTableLocked = false;  // 標記側表是否已鎖定(避免多線程競爭)bool transcribeToSideTable = false;  // 標記是否需要將內聯計數轉錄到側表isa_t oldisa;  // 保存舊的 isa 位域狀態isa_t newisa;  //新的 isa 位域狀態(待更新)oldisa = LoadExclusive(&isa().bits);  //原子加載當前 isa 位域的原子性(避免其他線程修改導致臟讀)if (variant == RRVariant::FastOrMsgSend) { //表示當前調用是“快速路徑”或“模擬 objc_msgSend發送 retain消息”(如直接調用 [obj retain])// These checks are only meaningful for objc_retain()// They are here so that we avoid a re-load of the isa.if (slowpath(oldisa.getDecodedClass(false)->hasCustomRR())) { //檢查對象的類是否覆蓋了自定義的引用計數邏輯(如 retain/release方法);若有,跳過默認邏輯,直接調用自定義實現ClearExclusive(&isa().bits);  // 釋放原子加載的鎖if (oldisa.getDecodedClass(false)->canCallSwiftRR()) {  //判斷是否可調用 Swift 的保留函數(swiftRetain),兼容 Swift 對象的引用計數管理return swiftRetain.load(memory_order_relaxed)((id)this);}return ((id(*)(objc_object *, SEL))objc_msgSend)(this, @selector(retain));}}if (slowpath(!oldisa.nonpointer)) {// a Class is a Class forever, so we can perform this check once// outside of the CAS loop//判斷元類,元類是類的類,無需被保留(無實例指向元類),因此直接返回自身if (oldisa.getDecodedClass(false)->isMetaClass()) {ClearExclusive(&isa().bits);return (id)this;}}//編譯器宏,指示是否啟用內聯引用計數(現代 iOS/macOS 系統默認啟用):若未啟用內聯引用計數(如舊版本或特殊配置),引用計數完全存儲在側表(SideTable)中,直接調用側表的 tryRetain或 retain方法
#if !ISA_HAS_INLINE_RC// No need for a CAS loop in this case; we aren't changing the ISA pointerClearExclusive(&isa().bits);if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;else return sidetable_retain(sideTableLocked);
#elsedo {transcribeToSideTable = false;newisa = oldisa;if (slowpath(!newisa.nonpointer)) {  // 重新檢查是否為非指針 isa(可能被其他線程修改)ClearExclusive(&isa().bits);if (tryRetain) return sidetable_tryRetain() ? (id)this : nil;else return sidetable_retain(sideTableLocked);}// don't check newisa.fast_rr; we already called any RR overridesif (slowpath(newisa.isDeallocating())) {  //對象正在釋放,禁止保留ClearExclusive(&isa().bits);if (sideTableLocked) {ASSERT(variant == RRVariant::Full);sidetable_unlock();}if (slowpath(tryRetain)) {return nil;} else {return (id)this;}}uintptr_t carry;newisa.bits = addc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc++ 原子增加extra_rcif (slowpath(carry)) {  //內聯引用計數溢出(extra_rc超過最大值)// newisa.extra_rc++ overflowedif (variant != RRVariant::Full) {  //非完整變體,直接處理溢出ClearExclusive(&isa().bits);return rootRetain_overflow(tryRetain);}// Leave half of the retain counts inline and // prepare to copy the other half to the side table.//準備轉錄到側表:保留一半內聯計數,另一半存側表if (!tryRetain && !sideTableLocked) sidetable_lock();  //鎖定側表(僅非嘗試保留時)sideTableLocked = true;transcribeToSideTable = true;newisa.extra_rc = RC_HALF;  //內聯部分保留一半newisa.has_sidetable_rc = true; //標記使用側表}} while (slowpath(!StoreExclusive(&isa().bits, &oldisa.bits, newisa.bits)));  //CAS重新提交isa位域if (variant == RRVariant::Full) {if (slowpath(transcribeToSideTable)) {// Copy the other half of the retain counts to the side table.sidetable_addExtraRC_nolock(RC_HALF);  //將剩余的一半引用計數添加到側表}if (slowpath(!tryRetain && sideTableLocked)) sidetable_unlock(); //非嘗試保留時解鎖側表} else {ASSERT(!transcribeToSideTable);ASSERT(!sideTableLocked);}
#endifreturn (id)this;
}

從上述代碼中,我們可以知道objc_object::rootRetain的工作流程以原子性增加對象引用計數為核心,代碼執行的流程大致如下:

首先初始化 sideTableLockedtranscribeToSideTable 狀態變量,并通過 LoadExclusive 原子加載當前 isa 位域(oldisa)。隨后進入 do-while 循環,通過 StoreExclusive 原子提交新 isa 位域(newisa),確保多線程下的原子性操作。循環中檢查 newisa 是否為非指針 isa(若為非指針則重置鎖并重試),并根據 tryRetain 標志選擇調用 sidetable_tryRetain(嘗試保留)或 sidetable_retain(強制保留)。若對象正在釋放(isDeallocating()),則清除鎖并根據條件返回 nil或對象自身。若內聯引用計數(extra_rc)溢出(carry 標志為真),則將部分計數轉錄到側表(標記 has_sidetable_rc)并調整內聯值(保留 RC_HALF)。最終提交 isa 更新后,同步側表數據(若為完整變體)并解鎖,返回對象自身完成引用計數增加。

相關的sidetable_tryRetain()方法

接下來我們再來看涉及到的sidetable_tryRetain()方法的源碼:

bool
objc_object::sidetable_tryRetain()
{
#if SUPPORT_NONPOINTER_ISA  //編譯器宏,指示是否支持非指針 isa(如小對象優化的 Tagged Pointer)ASSERT(!isa().nonpointer);  //若支持非指針 isa,則當前對象的 isa不能是指針類型(nonpointer標志為 false)。此斷言用于確保非指針 isa場景下,側表操作的正確性(避免對非指針 isa執行側表邏輯)
#endifSideTable& table = SideTables()[this];//SideTables():全局哈希表,存儲每個對象地址對應的 SideTable(通過對象地址哈希映射)。SideTable結構:每個 SideTable包含兩個核心結構:refcnts:引用計數映射表(RefcountMap),記錄對象被弱引用或臨時引用的次數;weak_table_t:弱引用表,存儲指向該對象的弱引用指針(如 __weak變量)。// NO SPINLOCK HERE// _objc_rootTryRetain() is called exclusively by _objc_loadWeak(), // which already acquired the lock on our behalf.// fixme can't do this efficiently with os_lock_handoff_s// if (table.slock == 0) {//     _objc_fatal("Do not call -_tryRetain.");// }bool result = true;//原子操作,嘗試在 refcnts中插入當前對象的條目。若條目已存在(it.second為 false),則直接獲取現有條目;若不存在(it.second為 true),則插入新條目,初始值為 SIDE_TABLE_RC_ONE(表示一次弱引用)auto it = table.refcnts.try_emplace(this, SIDE_TABLE_RC_ONE);auto &refcnt = it.first->second;//根據 try_emplace的結果(it.second)和當前引用計數的狀態(refcnt),分三種情況處理:if (it.second) { //情況一:插入新條目// there was no entry} else if (refcnt & SIDE_TABLE_DEALLOCATING) { //情況二:條目已存在且對象正在被釋放result = false;} else if (! (refcnt & SIDE_TABLE_RC_PINNED)) { //情況三:條目已存在且對象未被釋放refcnt += SIDE_TABLE_RC_ONE;}return result;
}

由此,我們可以看來,objc_object::sidetable_tryRetain() 函數通過側表(SideTable)嘗試為對象添加弱引用,流程如下:

首先斷言確保對象為指針類型(非小對象優化場景),獲取對象對應的側表;通過原子操作 try_emplace嘗試在側表的引用計數映射(refcnts)中插入當前對象的條目。若插入成功(新條目),初始化引用計數為 SIDE_TABLE_RC_ONE(一次弱引用);若條目已存在,檢查對象狀態:若對象正在釋放(deallocating 標志),則嘗試失敗;若未被固定(pinned 標志),則將引用計數加 1。最終返回是否成功嘗試保留對象(true或 false)。

retain底層工作流程總結

至此,我們可以知道retain底層的工作流程圖大致如下:

+----------------------+
|     objc_retain()    |
+----------------------+|v
+--------------------------+
| 檢查對象是否為 nil?        |
+--------------------------+|+-------+-------+|               |是(nil)           否|               |
返回 nil       +-----------------------------+|  調用 obj->retain()         |+-----------------------------+|v+----------------------------------------+| objc_object::retain()                  ||                                        || -> 是否啟用 Tagged Pointer?             || -> 是否使用 isa-optimized 引用計數?      |+----------------------------------------+|+------------+--------------+|                           |isa優化計數 YES                      NO+-----------------------------+     +-----------------------------+|  利用 isa 指針中位域計數器     |     |  使用 SideTable 哈希表        ||  直接在 isa 中的引用計數 +1    |     |  sidetable_retain()         |+-----------------------------+     +-----------------------------+|v+----------------------------------+| 加鎖 -> SideTable.refcnts[obj]+1 || 解鎖                             |+----------------------------------+

release

release的作用是減少對象的引用計數,若計數歸零則銷毀對象。release的底層邏輯:

  1. 檢查對象是否為小對象,若是則直接返回。
  2. 獲取 SideTable并減少引用計數。
  3. 若引用計數減至 0:調用 dealloc方法(釋放實例變量、關聯對象等);釋放 SideTable內存(若無其他對象使用)。
release源碼

在objc_906_main中,這部分源碼如下:

inline void
objc_object::release()
{ASSERT(!isTaggedPointer()); // 斷言非小對象rootRelease(true, RRVariant::FastOrMsgSend); // 調用核心釋放函數
}inline void
objc_object::release()
{ASSERT(!isTaggedPointer()); // 斷言非小對象// 快速路徑:無自定義釋放邏輯時,直接操作側表if (fastpath(!ISA()->hasCustomRR())) { // 元類無需釋放(無實例指向)if (!ISA()->isMetaClass())sidetable_release(); // 釋放側表中的引用計數return;}// 自定義釋放邏輯:通過消息發送調用類的 release 方法((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(release));
}
內聯函數rootRelease源碼

這部分源碼有很多地方的處理與rootRelease相同,有的不再一一贅述。

ALWAYS_INLINE bool 
objc_object::rootRelease()
{return rootRelease(true, RRVariant::Fast);
}ALWAYS_INLINE bool
objc_object::rootRelease(bool performDealloc, objc_object::RRVariant variant)
{if (slowpath(isTaggedPointer())) return false;bool sideTableLocked = false;  // 標記側表是否鎖定(避免多線程競爭)isa_t newisa, oldisa;  //保存新舊 isa 位域狀態oldisa = LoadExclusive(&isa().bits);  // 原子加載當前 isa 位域(多線程安全)if (variant == RRVariant::FastOrMsgSend) { //RRVariant::FastOrMsgSend:表示當前調用是“快速路徑”或“模擬 objc_msgSend發送 release消息”(優化調用流程)// These checks are only meaningful for objc_release()// They are here so that we avoid a re-load of the isa.if (slowpath(oldisa.getDecodedClass(false)->hasCustomRR())) { //這里跟rootRetain的判斷邏輯一樣,兼容OC和swiftClearExclusive(&isa().bits);if (oldisa.getDecodedClass(false)->canCallSwiftRR()) {swiftRelease.load(memory_order_relaxed)((id)this);return true;}((void(*)(objc_object *, SEL))objc_msgSend)(this, @selector(release));return true;}}if (slowpath(!oldisa.nonpointer)) {  //元類檢查(跟roorRetain一樣)// a Class is a Class forever, so we can perform this check once// outside of the CAS loopif (oldisa.getDecodedClass(false)->isMetaClass()) {ClearExclusive(&isa().bits);return false;}}#if !ISA_HAS_INLINE_RC// Without inline ref counts, we always use sidetablesClearExclusive(&isa().bits);return sidetable_release(sideTableLocked, performDealloc);
#else
retry:do {newisa = oldisa;if (slowpath(!newisa.nonpointer)) {ClearExclusive(&isa().bits);return sidetable_release(sideTableLocked, performDealloc);}if (slowpath(newisa.isDeallocating())) {ClearExclusive(&isa().bits);if (sideTableLocked) {ASSERT(variant == RRVariant::Full);sidetable_unlock();}return false;}// don't check newisa.fast_rr; we already called any RR overridesuintptr_t carry;newisa.bits = subc(newisa.bits, RC_ONE, 0, &carry);  // extra_rc--if (slowpath(carry)) {// don't ClearExclusive()goto underflow;}} while (slowpath(!StoreReleaseExclusive(&isa().bits, &oldisa.bits, newisa.bits)));if (slowpath(newisa.isDeallocating()))goto deallocate;if (variant == RRVariant::Full) {if (slowpath(sideTableLocked)) sidetable_unlock();} else {ASSERT(!sideTableLocked);}return false;//引用計數溢出處理(借位邏輯)underflow:// newisa.extra_rc-- underflowed: borrow from side table or deallocate(借位:從側表借位或觸發釋放)// abandon newisa to undo the decrementnewisa = oldisa;  // 回滾到舊狀態if (slowpath(newisa.has_sidetable_rc)) {  //側表有引用計數if (variant != RRVariant::Full) {  //非完整變體,直接處理借位ClearExclusive(&isa().bits);return rootRelease_underflow(performDealloc);}// Transfer retain count from side table to inline storage.if (!sideTableLocked) {ClearExclusive(&isa().bits);sidetable_lock();sideTableLocked = true;// Need to start over to avoid a race against // the nonpointer -> raw pointer transition.oldisa = LoadExclusive(&isa().bits);goto retry;}// Try to remove some retain counts from the side table.(從側表借位(最多借 RC_HALF))auto borrow = sidetable_subExtraRC_nolock(RC_HALF);bool emptySideTable = borrow.remaining == 0; // we'll clear the side table if no refcounts remain there(側表是否清空)if (borrow.borrowed > 0) {  //成功借位// Side table retain count decreased.// Try to add them to the inline count.bool didTransitionToDeallocating = false;newisa.extra_rc = borrow.borrowed - 1;  // redo the original decrement too 調整內聯計數(借位后減1)newisa.has_sidetable_rc = !emptySideTable;  //更新側表標記bool stored = StoreReleaseExclusive(&isa().bits, &oldisa.bits, newisa.bits);if (!stored && oldisa.nonpointer) {  //提交失敗,重試// Inline update failed.// Try it again right now. This prevents livelock on LL/SC // architectures where the side table access itself may have // dropped the reservation.uintptr_t overflow;newisa.bits =addc(oldisa.bits, RC_ONE * (borrow.borrowed-1), 0, &overflow);newisa.has_sidetable_rc = !emptySideTable;if (!overflow) {stored = StoreReleaseExclusive(&isa().bits, &oldisa.bits, newisa.bits);if (stored) {didTransitionToDeallocating = newisa.isDeallocating();}}}if (!stored) {// Inline update failed.// Put the retains back in the side table.ClearExclusive(&isa().bits);sidetable_addExtraRC_nolock(borrow.borrowed);//重新加載isa并重試oldisa = LoadExclusive(&isa().bits);goto retry;}// Decrement successful after borrowing from side table.(借位成功,返回結果)if (emptySideTable)sidetable_clearExtraRC_nolock();//側表清空則清除if (!didTransitionToDeallocating) {//未觸發釋放則解鎖側表if (slowpath(sideTableLocked)) sidetable_unlock();return false;}}else {//側表無引用計數,觸發釋放// Side table is empty after all. Fall-through to the dealloc path.(跳轉到釋放路徑)}}
deallocate:// Really deallocate.ASSERT(newisa.isDeallocating());ASSERT(isa().isDeallocating());if (slowpath(sideTableLocked)) sidetable_unlock();//解鎖側表__c11_atomic_thread_fence(__ATOMIC_ACQUIRE);//內存屏障,確保操作前的操作可見if (performDealloc) {this->performDealloc();//執行對象的dealloc方法}return true;//釋放成功
#endif // ISA_HAS_INLINE_RC
}

由此可見, release方法的底層工作流程以==原子性減少對象引用計數為核心。首先通過 isTaggedPointer() 檢查對象是否為小對象(如 int、小 NSNumber等),若是則直接跳過引用計數管理;接著檢查類是否覆蓋自定義 release邏輯(hasCustomRR),若有則調用自定義實現(如 Swift 的 swiftRelease 或通過 objc_msgSend 發送 release 消息);若為元類(isMetaClass)則直接返回(元類無實例指向,無需釋放);對于普通對象,通過 CAS(Compare-And-Swap)原子操作(LoadExclusive/StoreReleaseExclusive)安全減少內聯引用計數(存儲于 isa位域的 extra_rc字段);若內聯計數溢出(extra_rc 減至負數),則從側表(SideTable)借位(最多借 RC_HALF)并調整內聯計數;最終若引用計數歸零且無法借位,標記對象為釋放狀態(isDeallocating),執行內存屏障確保可見性后調用 dealloc 釋放內存。

小結

??retain/release是MRC模式下引用計數的操作入口,我們可以通過它們直接操作引用計數字段。雖然現在xcode啟用ARC計數,十分方便,但我們還是有必要了解MRC下相關的手動引用計數管理,這有利于我們更好地掌握引用計數機制。

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

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

相關文章

文件同步神器-rsync命令講解

rsync 是一個強大的文件同步與傳輸工具,廣泛用于本地或遠程服務器之間的高效文件備份、鏡像或同步。其核心優勢是通過增量傳輸?(僅傳輸文件差異部分)和壓縮減少數據傳輸量,同時支持保留文件元數據(如權限、時間戳、所…

Rust: 工具鏈版本更新

遇到 cargo build --release 錯誤,比如,當前 Rust 工具鏈版本(1.78.0)低于依賴項所需的最低版本(部分依賴要求 ≥1.82.0)。以下是系統化的解決方案: 🔧 一、升級 Rust 工具鏈&#x…

Prompt-to-Prompt| 修改Attention會有“反向傳播”或梯度計算?

需要注意的幾個問題:額外計算開銷:Cross-Attention Control原因:Prompt-to-Prompt的編輯方法需要動態干預交叉注意力(Cross-Attention)層的權重,這會引入額外的計算和顯存占用:需要緩存注意力矩…

電商API接口的優勢、數據采集方法及功能說明

一、電商API接口的核心優勢1. 高效性與準確性數據采集效率:API通過標準化參數(如商品ID、類目)直接獲取結構化數據(JSON/XML),無需解析HTML,減少誤差。例如,采集1000條商品信息&…

iOS企業簽名掉簽,iOS企業簽名掉簽了怎么辦?

不能上架到App Store的iOS應用 ,幾乎每一個開發者的選擇都是通過iOS簽名這種內測渠道來完成APP的上架任務,最常用的就是企業簽名、超級簽名以及TF上架,其中最受歡迎的當屬于企業簽名了。不過企業簽名會出現掉簽的現象,那么企業簽名…

存儲成本深度優化:冷熱分層與生命周期管理——從視頻平臺年省200萬實踐解析智能存儲架構

一、冷熱分層:存儲成本優化的核心邏輯1.1 數據訪問的“二八定律”據行業統計,80%的訪問集中在20%的熱數據上,而超過90天的歷史數據訪問頻率下降70%以上。某視頻平臺存儲超10PB媒體文件,未分層前年存儲成本高達680萬元,…

Java設計模式之《備忘錄模式》

目錄 1. 概念 1.1、定義 1.2、適用場景 2、角色劃分 3、實現 1、Originator(發起人) 2、Memento(備忘錄) 3、Caretaker(管理者) 4、使用示例 4、優缺點 4.1、優點 4.2、缺點 前言 備忘錄模式是…

SpringBoot 多環境配置

在實際項目開發中,不同環境往往有不同的配置需求: 開發環境(dev):本地調試,連接測試數據庫;測試環境(test):接口聯調,接近真實場景;生…

延凡智慧醫院數字孿生平臺

延凡智慧醫院數字孿生平臺是延凡科技依托物聯網、數字孿生、AI 算法及邊緣計算技術打造的醫療場景全要素數字化解決方案,通過構建醫院物理實體與虛擬空間的實時映射,實現醫療資源優化、運營效率提升及患者體驗升級。一、平臺價值(一&#xff…

談談WebAssembly、PWA、Web Workers的作用和場景

WebAssembly、PWA 和 Web Workers 是現代 Web 開發中提升性能、擴展能力的重要技術,各自解決不同場景的問題,以下結合實際使用經驗分析:一、WebAssembly(Wasm):高性能代碼執行作用:WebAssembly …

嵌入式第十八課!!數據結構篇入門及單向鏈表

在前幾章對C語言的學習中,我們學到了:基本的C語法和簡單算法面向過程的編程思想而在數據結構這一篇章,我們將要學習:常用的數據存儲結構算法面向對象的編程思想數據結構在正式開始學習之前,我們先來了解一下什么是數據…

win10任務欄出問題了,原來是wincompressbar導致的

問題描述兄弟們客戶說自己電腦現在有問題了,任務欄顯示的都不對,和之前的都不一樣,現在使用起來非常難受,我們來看一下,這到底是什么問題吧!到客戶現場,查看發現,客戶桌面系統最底下…

FFmpegHandler 功能解析,C語言程序化設計與C++面向對象設計的核心差異

FFmpegHandler 功能解析 本文件記錄了關于 FFmpegHandler 類中核心函數工作流程的詳細解釋。Q: FFmpeg逐幀解碼,FFmpegHandler::openVideo 和 FFmpegHandler::readAVFrame 這兩個函數都分別做了什么? A: 可以把整個過程想象成“準備播放一部電影”&#…

Codeforces Round 1039 (Div. 2) A-C

A. Recycling Center題目大意 給你n個垃圾袋,每個垃圾袋有一個重量 在每秒鐘,你可以選擇一個垃圾袋,如果他的重量小于等于c,那么你可以不花費硬幣丟掉它 當你丟掉一個垃圾袋后,其他垃圾袋在這一秒重量會翻倍 問最少花費…

【設計模式】 原則

單一職責原則 對于一個類而言,有且僅有一個引起他變化的原因或者說,一個類只負責一個職責 如果一個類承擔的職責過多,那么這些職責放在一起耦合度太高了,一個職責的變化可能會影響這個類其他職責的能力。 所以我們在做軟件設計的時…

windows11右鍵菜單新增項增加drawio文件,使用draw.io

目錄1.新建空白模板2.建立注冊表文件1.新建空白模板 這里我們的模板文件路徑為 D:\Software\drawio\template.drawio 2.建立注冊表文件 首先新建一個.txt文件,我這里取名為menulize.txt,然后將下面的內容復制到.txt文件中 Windows Registry Editor Ver…

解鎖網頁魔法:零基礎HTML通關秘籍

文章目錄**解鎖網頁魔法:零基礎HTML通關秘籍**HTML 基礎目標HTML 結構認識 HTML 標簽HTML 文件基本結構標簽層次結構快速生成代碼框架HTML 常見標簽注釋標簽注釋的原則標題標簽: h1-h6段落標簽: p換行標簽:br綜合案例: 展示博客超鏈接標簽: a表格標簽**基…

類似 Pixso 但更側重「網頁 / 軟件界面設計」「前后端可視化開發」的工具

從 GoView 的 Demo 功能來看,它主要聚焦于數據可視化大屏的低代碼搭建,更側重數據圖表配置和頁面布局,沒有類似 Pixso 的在線 UI 設計(如矢量繪圖、組件樣式精細化設計)功能,其核心是通過預設組件快速構建數…

MySQL--組從復制的詳解及功能演練

2.MySQL的組從復制 2.1 配置mastesr [rootmysqlaa ~]# vim /etc/my.cnf [mysqld] server-id10 datadir/data/mysql socket/data/mysql/mysql.sock default_authentication_pluginmysql_native_password log-binmysql-bin[rootmysqlaa ~]# /etc/init.d/mysqld restart# 進入數據…

JavaScript將String轉為base64 筆記250802

JavaScript將String轉為base64 筆記250802 在 JavaScript 中將字符串轉換為 Base64 編碼有多種方法,每種方法都有其適用場景。下面我將全面介紹這些方法,包括處理 ASCII 字符、Unicode 字符以及性能優化方案。 基礎方法:btoa() 基本用法&a…