首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

Objective-C Block 深度解析:捕獲機制、存儲類型與循環引用

     編輯:關於C語言

Block 基礎測試題(文末有答案)

五道經典 Block 題目,涉及棧/堆存儲、變量捕獲、ARC 與非 ARC 行為差異。請先思考再看文末答案。

Block 是什麼?

Block 是 C 語言擴展,本質是帶有自動變量(局部變量)的匿名函數。它允許在不聲明類的情況下,用函數式風格保存上下文狀態,極大簡化了回調、代理等場景的代碼。

Block 內部實現原理

使用 clang -rewrite-objc 可將 Block 轉為可讀的 C++ 代碼。核心結構:

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 val;
};

Block 實際上是一個 Objective-C 對象(有 isa 指針),捕獲的自動變量以值傳遞方式複製到 Block 結構體中。

自動變量捕獲機制

int val = 10;
void (^blk)(void) = ^{ printf("val=%d\n", val); };
val = 2;
blk();  // 輸出 10,而非 2

Block 捕獲的是創建時刻的瞬時值。捕獲的自動變量在 Block 內部實質變為 const,不可修改,否則編譯報錯。

__block 修飾符:打破只讀限制

__block int val = 10;
void (^blk)(void) = ^{ val = 1; };

加上 __block 後,變量會被包裝成一個結構體 __block_byref_val_0,包含 __forwarding 指針指向自身。這使得 Block 可修改變量,且無論 Block 在棧上還是複製到堆上,都能正確訪問同一份內存。

Block 的三種存儲區域

類型說明
_NSConcreteStackBlock棧上,函數內部且捕獲自動變量時(非 ARC 默認)
_NSConcreteGlobalBlock全局區,函數外部定義,或函數內部但未捕獲任何自動變量
_NSConcreteMallocBlock堆上,調用 copy 後,或 ARC 環境下捕獲自動變量自動複製到堆

關鍵行為總結

  • 全局 Block 調用 copy 無效果;棧 Block copy 會複製到堆;堆 Block copy 僅增加引用計數。
  • ARC 環境下,捕獲自動變量的 Block 自動生成在堆上;非 ARC 下仍在棧上,需手動 copy。
  • 無論何處,多調用 copy 無副作用,ARC 會自動管理釋放。

__block 變量的存儲遷移

當 Block 被複製到堆時,它捕獲的 __block 變量也會一同複製到堆。結構體中的 __forwarding 指針確保棧上和堆上的 Block 都能正確訪問同一份變量內存。

對象捕獲與循環引用

Block 會強持有捕獲的 Objective-C 對象。若對象又持有 Block,會產生循環引用。解決方案:

  1. 推薦:使用 __weak 修飾符。__weak typeof(self) weakSelf = self;
  2. 使用 __block 並在 Block 內置 nil:__block typeof(self) tmp = self; ... tmp = nil;(需記得置 nil,不推薦)。

測試題答案

ABABB

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved