Apple 在 C/C++/Objective-C 中引入 Block(塊) 擴展,賦予這些語言類 Lambda 的閉包能力。本文從語法、捕獲語義、底層實現、內存管理四個維度,將 Block 與 C++11 Lambda 並列剖析,幫助開發者建立跨語言的統一認知。
// Objective-C Block
^{ printf("Hello, World!\n"); } ();
// C++11 Lambda
[] { std::cout << "Hello, World" << std::endl; } ();
語法差異:Block 用 ^,Lambda 用 []。編譯器會為每個匿名函數生成內部名稱,格式為 __{$Scope}_block_func_{$index}。
int i = 1024;
void (^blk)(void) = ^{ printf("%d\n", i); }; // 直接訪問外部 i
blk(); // 輸出 1024
Block 默認以值傳遞捕獲自動變量,捕獲瞬間的值被拷貝進 Block 結構體,Block 內部不可修改。
int i = 1024;
auto func = [] { printf("%d\n", i); }; // 錯誤:'i' 未被捕獲
auto func = [=] { printf("%d\n", i); }; // 正確:[=] 以值捕獲所有引用變量
auto func = [&] { i = 0; printf("%d\n", i); }; // 以引用捕獲,可修改
Lambda 捕獲列表選項:
[] 無捕獲[=] 值捕獲所有[&] 引用捕獲所有[=, &x] 默認值捕獲,x 引用捕獲[x] 僅值捕獲 x[this] 捕獲 this 指標__block vs mutable/引用捕獲| 場景 | Objective-C Block | C++11 Lambda |
|---|---|---|
| 值捕獲後修改 | 編譯錯誤,需加 __block int i | 編譯錯誤,需加 mutable 或改引用捕獲 |
| 引用捕獲修改 | 不適用(無引用捕獲語法) | 直接 [&] 或 [&i] 即可 |
本質原因:兩者默認值捕獲均將變量拷貝為內部成員,修改只影響副本。__block / 引用捕獲通過間接層(指標/引用)指向原變量,實現共享修改。
編譯器將 Block 降級為 C 結構體 + 函數指標:
struct __block_impl {
void *isa; // Objective-C 對象頭
int Flags;
int Reserved;
void *FuncPtr; // 指向實際執行函數
};
struct __main_block_impl_0 { // 具體 Block 實例結構
struct __block_impl impl;
struct __main_block_desc_0 *Desc;
int i; // 捕獲的變量(值拷貝)
// 構造函數初始化 i = _i
};
執行時將結構體自身作為 __cself 傳給 FuncPtr 指向的函數,實現閉包環境訪問。
__block 變量的進階佈局:__Block_byref_x_x加 __block 後,變量被包裹在單獨結構體中:
struct __Block_byref_i_0 {
void *__isa;
__Block_byref_i_0 *__forwarding; // 關鍵:指向自身或堆上副本
int __flags;
int __size;
int i; // 真實值
};
Block 結構體改為持有 __Block_byref_i_0 *,通過 __forwarding 間接訪問,實現棧/堆同步。
__forwarding 機制[blk copy] 將 Block 結構體複製到堆(引用計數管理)。__block 變量結構體複製到堆,並修改棧上原結構體的 __forwarding 指向堆上副本。__forwarding 共享同一個 __block 變量,修改即時可見。| 維度 | Objective-C Block | C++11 Lambda |
|---|---|---|
| 語法標記 | ^ | [] |
| 默認捕獲 | 值捕獲(只讀) | 無捕獲(需顯式指定) |
| 可修改捕獲 | __block 修飾變量 | mutable (值捕獲) / [&] (引用捕獲) |
| 實現本質 | 結構體 + 函數指標,isa 融入 OC 對象體系 | 仿函數對象,編譯器生成唯一類型類 |
| 內存管理 | 手動 copy/release,ARC 自動處理 | RAII,隨對象生命週期自動析構 |
| 跨線程安全 | 需手動 copy 到堆 | 捕獲引用需自行保證生命週期,值捕獲天然安全 |
Block 與 Lambda 雖語法不同,但本質同為「環境捕獲 + 可調用實體」。理解其底層結構體佈局、__forwarding 指針技巧、棧/堆遷移邏輯,不僅能寫出正確高效的閉包代碼,更能洞悉現代語言運行時的通用設計智慧。