Objective-C Block內(nèi)存布局與實(shí)現(xiàn)原理詳解
1. Block內(nèi)存布局深度解析在編程領(lǐng)域Block是一種特殊的語法結(jié)構(gòu)它允許我們將代碼塊作為參數(shù)傳遞或存儲在變量中。理解Block的內(nèi)存布局對于編寫高效、安全的代碼至關(guān)重要。今天我們就來徹底拆解Block在內(nèi)存中的組織方式。Block本質(zhì)上是一個(gè)對象它包含了執(zhí)行代碼和捕獲的變量。在內(nèi)存中Block由多個(gè)部分組成每個(gè)部分都有其特定的作用。我們先來看一個(gè)簡單的Block示例void (^myBlock)(void) ^{ NSLog(This is a block); };這個(gè)看似簡單的Block在內(nèi)存中其實(shí)有著復(fù)雜的結(jié)構(gòu)。接下來我們將從底層角度分析Block的內(nèi)存布局。2. Block內(nèi)存布局的核心結(jié)構(gòu)2.1 Block的基本內(nèi)存布局一個(gè)Block在內(nèi)存中的典型布局包含以下幾個(gè)關(guān)鍵部分Block描述符(Block Descriptor)捕獲的變量(Captured Variables)函數(shù)指針(Invoke Function Pointer)標(biāo)志位(Flags)在64位系統(tǒng)上一個(gè)Block的頭部通常占用24字節(jié)的內(nèi)存空間。我們可以用以下結(jié)構(gòu)體來表示struct Block_layout { void *isa; volatile int32_t flags; int32_t reserved; void (*invoke)(void *, ...); struct Block_descriptor *descriptor; // 捕獲的變量會跟隨在這里 };2.2 各組成部分詳解2.2.1 isa指針每個(gè)Block對象都包含一個(gè)isa指針指向它的類。這與其他Objective-C對象的行為一致。根據(jù)Block的類型不同isa指針可能指向_NSConcreteGlobalBlock全局Block_NSConcreteStackBlock棧Block_NSConcreteMallocBlock堆Block2.2.2 flags字段flags字段包含了Block的各種屬性信息常見的標(biāo)志位包括enum { BLOCK_HAS_COPY_DISPOSE (1 25), // 有copy和dispose輔助函數(shù) BLOCK_HAS_CTOR (1 26), // 有C構(gòu)造函數(shù) BLOCK_IS_GLOBAL (1 28), // 是全局Block BLOCK_HAS_STRET (1 29), // 有stret返回值 BLOCK_HAS_SIGNATURE (1 30) // 有簽名信息 };2.2.3 invoke函數(shù)指針invoke是一個(gè)函數(shù)指針指向Block的實(shí)現(xiàn)代碼。當(dāng)Block被調(diào)用時(shí)實(shí)際上就是通過這個(gè)指針來執(zhí)行對應(yīng)的函數(shù)。2.2.4 descriptor描述符descriptor指針指向Block的描述信息包含以下內(nèi)容struct Block_descriptor { unsigned long int reserved; unsigned long int size; void (*copy)(void *dst, void *src); void (*dispose)(void *); const char *signature; };3. Block的三種內(nèi)存類型3.1 全局Block(NSGlobalBlock)全局Block具有以下特點(diǎn)不捕獲任何外部變量生命周期與程序相同存儲在程序的全局?jǐn)?shù)據(jù)區(qū)多次使用同一個(gè)全局Block實(shí)際上是使用同一個(gè)對象示例void (^globalBlock)(void) ^{ NSLog(This is a global block); };3.2 棧Block(NSStackBlock)棧Block的特點(diǎn)捕獲了外部變量存儲在棧上生命周期與所在函數(shù)相同函數(shù)返回后棧Block就無效了示例int a 10; void (^ __weak stackBlock)(void) ^{ NSLog(This is a stack block: %d, a); };注意在ARC環(huán)境下編譯器會自動將棧Block復(fù)制到堆上所以很難直接觀察到棧Block。3.3 堆Block(NSMallocBlock)堆Block的特點(diǎn)由棧Block通過copy操作創(chuàng)建存儲在堆上需要手動管理內(nèi)存(在MRC下)可以跨函數(shù)使用示例int b 20; void (^heapBlock)(void) [^{ NSLog(This is a heap block: %d, b); } copy];4. Block的內(nèi)存管理4.1 Block的copy操作Block的copy操作會根據(jù)不同類型有不同的行為全局Blockcopy操作什么也不做直接返回原Block棧Blockcopy操作會將Block從棧復(fù)制到堆上堆Blockcopy操作會增加引用計(jì)數(shù)在MRC環(huán)境下我們需要手動管理Block的內(nèi)存// MRC下 void (^block)(void) [^{ /* ... */ } copy]; // 使用完后需要release [block release];在ARC環(huán)境下編譯器會自動插入適當(dāng)?shù)腸opy/release調(diào)用。4.2 Block的循環(huán)引用問題由于Block會強(qiáng)引用它捕獲的對象因此容易造成循環(huán)引用。常見解決方案使用__weak修飾符__weak typeof(self) weakSelf self; self.block ^{ [weakSelf doSomething]; };使用__block修飾符(僅限MRC)__block id blockSelf self; self.block ^{ [blockSelf doSomething]; blockSelf nil; // 打破循環(huán) };5. Block的變量捕獲機(jī)制5.1 自動變量捕獲Block會捕獲使用的外部變量但默認(rèn)情況下是以const方式捕獲的即在Block內(nèi)部不能修改這些變量int value 10; void (^block)(void) ^{ // value 20; // 編譯錯(cuò)誤 NSLog(%d, value); };5.2 __block變量使用__block修飾的變量可以在Block內(nèi)部修改__block int mutableValue 10; void (^block)(void) ^{ mutableValue 20; // 可以修改 NSLog(%d, mutableValue); };在底層__block變量會被包裝成一個(gè)結(jié)構(gòu)體struct __Block_byref_mutableValue_0 { void *__isa; __Block_byref_mutableValue_0 *__forwarding; int __flags; int __size; int mutableValue; };5.3 對象類型的捕獲當(dāng)Block捕獲對象時(shí)會根據(jù)修飾符不同而有不同的行為默認(rèn)是強(qiáng)引用id obj [[NSObject alloc] init]; void (^block)(void) ^{ NSLog(%, obj); // 強(qiáng)引用obj };使用__weak修飾符__weak id weakObj obj; void (^block)(void) ^{ NSLog(%, weakObj); // 弱引用obj };6. Block的底層實(shí)現(xiàn)原理6.1 Block的編譯轉(zhuǎn)換編譯器會將Block轉(zhuǎn)換為一個(gè)結(jié)構(gòu)體和一個(gè)函數(shù)。例如int a 10; void (^block)(void) ^{ NSLog(%d, a); };會被轉(zhuǎn)換為類似下面的代碼struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0 *Desc; int a; }; static void __main_block_func_0(struct __main_block_impl_0 *__cself) { int a __cself-a; NSLog(%d, a); }6.2 Block的調(diào)用機(jī)制當(dāng)調(diào)用一個(gè)Block時(shí)實(shí)際上是調(diào)用了Block結(jié)構(gòu)體中的FuncPtr函數(shù)指針并將Block自身作為參數(shù)傳遞// block-invoke(block, ...);這就是為什么在Block內(nèi)部可以訪問到捕獲的變量 - 因?yàn)樗鼈兌即鎯υ贐lock結(jié)構(gòu)體中。7. 實(shí)際應(yīng)用中的注意事項(xiàng)7.1 性能優(yōu)化建議避免在循環(huán)中創(chuàng)建Block特別是捕獲了大量變量的Block對于不捕獲變量的Block使用全局Block合理使用__weak避免不必要的強(qiáng)引用7.2 調(diào)試技巧使用po [block copy]可以在lldb中查看Block的詳細(xì)信息通過xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc file.m命令可以查看Block的C實(shí)現(xiàn)使用Instruments的Allocations工具可以跟蹤Block的內(nèi)存分配情況7.3 常見問題排查EXC_BAD_ACCESS錯(cuò)誤可能是使用了已經(jīng)釋放的棧Block內(nèi)存泄漏檢查是否有循環(huán)引用變量值不符合預(yù)期確認(rèn)變量捕獲方式是否正確8. 不同語言中的Block實(shí)現(xiàn)對比8.1 Objective-C與Swift閉包比較雖然Swift的閉包與Objective-C的Block概念相似但在實(shí)現(xiàn)上有一些差異Swift閉包不區(qū)分全局/棧/堆閉包Swift的捕獲列表語法更明確Swift閉包默認(rèn)捕獲的變量是可變的8.2 C lambda表達(dá)式C11引入的lambda表達(dá)式與Block類似但有以下區(qū)別lambda可以按值或按引用捕獲變量lambda可以有模板參數(shù)lambda沒有自動的內(nèi)存管理9. 高級話題Block的ABI與跨平臺兼容性Block的ABI(應(yīng)用程序二進(jìn)制接口)在不同平臺上有細(xì)微差異iOS/macOS使用統(tǒng)一的Block ABI其他平臺可能需要特殊的編譯器標(biāo)志跨語言調(diào)用需要注意內(nèi)存管理和調(diào)用約定在編寫跨平臺代碼時(shí)需要特別注意Block的內(nèi)存管理方式和調(diào)用約定。10. 實(shí)戰(zhàn)案例自定義Block內(nèi)存分配器對于需要高性能的場景我們可以實(shí)現(xiàn)自定義的Block內(nèi)存分配器#import Block.h typedef void (^CustomBlock)(void); CustomBlock CreateCustomBlock(void (^original)(void)) { // 獲取原始Block的大小 size_t size Block_size(original); // 分配內(nèi)存 CustomBlock newBlock Block_copy(original); // 自定義初始化 // ... return newBlock; }這種技術(shù)可以用于特殊的內(nèi)存管理策略或性能優(yōu)化場景。

相關(guān)新聞

步進(jìn)電機(jī)原理、驅(qū)動與工程應(yīng)用全解析:從STM32控制到Eplan設(shè)計(jì)

步進(jìn)電機(jī)原理、驅(qū)動與工程應(yīng)用全解析:從STM32控制到Eplan設(shè)計(jì)

1. 從“脈沖”到“步進(jìn)”:一個(gè)被低估的精密執(zhí)行者如果你拆開過一臺老式的針式打印機(jī),或者仔細(xì)觀察過3D打印機(jī)的打印頭運(yùn)動,大概率會看到一個(gè)帶著幾根線、轉(zhuǎn)動起來一頓一頓的電機(jī)。沒錯(cuò),那就是步進(jìn)電機(jī)。在很多工程師和愛好者的第一…

2026/7/30 4:31:47 閱讀更多
C++實(shí)戰(zhàn):從零構(gòu)建文字冒險(xiǎn)游戲“騙子酒館”的完整指南

C++實(shí)戰(zhàn):從零構(gòu)建文字冒險(xiǎn)游戲“騙子酒館”的完整指南

1. 項(xiàng)目概述:從“騙子酒館”到C實(shí)戰(zhàn)演練 最近在社區(qū)里看到不少朋友在討論用C做些有趣的小項(xiàng)目來練手,從經(jīng)典的貪吃蛇、俄羅斯方塊,到一些需要點(diǎn)算法和設(shè)計(jì)模式支撐的復(fù)雜游戲。今天我想分享一個(gè)我個(gè)人覺得特別有意思的練手項(xiàng)目——“騙子酒館…

2026/7/30 5:31:51 閱讀更多
Harris角點(diǎn)檢測算法原理與Matlab實(shí)戰(zhàn)實(shí)現(xiàn)指南

Harris角點(diǎn)檢測算法原理與Matlab實(shí)戰(zhàn)實(shí)現(xiàn)指南

在圖像處理與計(jì)算機(jī)視覺項(xiàng)目中,角點(diǎn)檢測一直是基礎(chǔ)且關(guān)鍵的技術(shù)環(huán)節(jié)。無論是做圖像匹配、目標(biāo)跟蹤還是三維重建,Harris角點(diǎn)檢測算法都因其穩(wěn)定性和高效性成為首選方案之一。很多開發(fā)者在實(shí)際應(yīng)用時(shí),往往卡在參數(shù)調(diào)優(yōu)和效果驗(yàn)證環(huán)節(jié)&#xff0…

2026/7/30 5:31:51 閱讀更多
數(shù)字IC設(shè)計(jì)基礎(chǔ):串并轉(zhuǎn)換電路原理與Verilog實(shí)現(xiàn)詳解

數(shù)字IC設(shè)計(jì)基礎(chǔ):串并轉(zhuǎn)換電路原理與Verilog實(shí)現(xiàn)詳解

1. 項(xiàng)目概述:從“手撕代碼”到數(shù)字世界的橋梁搭建 在數(shù)字集成電路(Digital IC)的設(shè)計(jì)與驗(yàn)證面試、筆試乃至日常工作中,“手撕代碼”是一個(gè)繞不開的經(jīng)典環(huán)節(jié)。它考驗(yàn)的不僅僅是編碼能力,更是對數(shù)字電路底層邏輯、時(shí)序概…

2026/7/30 5:31:51 閱讀更多
FastAPI + Tortoise-ORM + Elasticsearch 實(shí)戰(zhàn):職位數(shù)據(jù)全量同步到 ES 的設(shè)計(jì)

FastAPI + Tortoise-ORM + Elasticsearch 實(shí)戰(zhàn):職位數(shù)據(jù)全量同步到 ES 的設(shè)計(jì)

項(xiàng)目實(shí)踐:FastAPI Elasticsearch 職位數(shù)據(jù)全量同步FastAPI Tortoise-ORM Elasticsearch 實(shí)戰(zhàn):職位數(shù)據(jù)全量同步到 ES 的設(shè)計(jì)意識與踩坑復(fù)盤一、前言介紹1.1 功能定位1.2 數(shù)據(jù)模型總覽1.3 同步流程總覽二、環(huán)境準(zhǔn)備2.1 依賴與 ES 客戶端2.2 索引與運(yùn)行…

2026/7/30 5:31:51 閱讀更多
Agentic AI系統(tǒng)設(shè)計(jì)模式實(shí)戰(zhàn):策略、觀察者與狀態(tài)模式解析

Agentic AI系統(tǒng)設(shè)計(jì)模式實(shí)戰(zhàn):策略、觀察者與狀態(tài)模式解析

1. 從零理解Agentic AI系統(tǒng)的設(shè)計(jì)模式核心第一次接觸Agentic AI系統(tǒng)時(shí),我被那些看似復(fù)雜的交互邏輯繞得頭暈。直到把設(shè)計(jì)模式這個(gè)"腳手架"搭起來,才發(fā)現(xiàn)原來大模型驅(qū)動的智能體開發(fā)可以如此條理清晰。這就像玩樂高,單個(gè)積木&#x…

2026/7/30 5:31:51 閱讀更多
[GESP202606 四級] 掃雷

[GESP202606 四級] 掃雷

B4557 [GESP202606 四級] 掃雷 https://www.luogu.com.cn/problem/B4557 中國計(jì)算機(jī)學(xué)會(CCF)2026年6月C四級講解——掃雷 https://www.bilibili.com/video/BV1MCMg6AEXR/ B4557 [GESP202606 四級] 掃雷 https://www.bilibili.com/video/BV1ZKTj6ZEVh/ 2…

2026/7/30 0:01:06 閱讀更多