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

Objective-C Block 完整指南:語法、捕獲語義、記憶體管理與實戰模式

     編輯:關於C語言

Objective-C Block 完整指南:語法、捕獲語義、記憶體管理與實戰模式

Block(閉包)是 Objective-C 2.0 引入的核心特性,將可執行代碼與其捕獲的變量環境封裝為一級對象。它廣泛應用於異步回調、集合遍歷、多線程併發、動畫完成塊等場景。本文系統梳理 Block 語法、變量捕獲規則、記憶體模型(棧/堆/全域)、__block 修飾符、循環引用解決方案與最佳實踐模式。

Block 基礎語法

宣告與定義

// 完整宣告語法:回傳型別 (^變數名)(參數列表)
void (^blockVoidVoid)(void);           // 無參無回傳
int (^blockIntVoid)(void);             // 無參有回傳
void (^blockVoidInt)(int);             // 有參無回傳
int (^blockIntIntInt)(int, int);       // 多參有回傳

// 定義賦值:使用 ^ 運算子
blockVoidVoid = ^(void) {
    NSLog(@"執行了 blockVoidVoid");
};

blockIntIntInt = ^(int a, int b) {
    NSLog(@"執行了 blockIntIntInt");
    return a + b;
};

直接內聯定義

void (^block)(int a) = ^(int a) {
    NSLog(@"%d", a);
};
block(3);  // 輸出 3

作為函數參數與回傳值

// 參數
void executeBlock(void (^block)(void)) {
    block();
}

// 回傳
int (^createAdder(int base))(int) {
    return ^(int x) { return base + x; };
}

變量捕獲機制深度解析

預設:值捕獲

Block 定義時「拍攝快照」,捕獲區域變量的當前值(按值複製)。Block 內部無法修改外部變量。

int num = 10;
void (^myBlock)(void) = ^(void) {
    NSLog(@"num = %d", num);  // 只能讀取,捕獲值 10
    // num = 20;  // 編譯錯誤
};
myBlock();  // 輸出: num = 10

__block:引用捕獲

加上 __block 修飾符,變量改為引用捕獲,Block 內可修改,且修改對外部可見。編譯器會將該變量包裝在一個結構體中,放在堆上管理。

__block int num = 10;
void (^myBlock)(void) = ^(void) {
    NSLog(@"num = %d", num);  // 10
    num = 20;  // 允許修改
};
myBlock();
NSLog(@"num = %d", num);  // 20,外部也被修改

Block 記憶體模型:三種儲存域

棧 Block 升級為堆 Block

@interface MyClass : NSObject
@property (copy) void (^block)(void);
@end

@implementation MyClass
- (void)setup {
    // self 持有 block,block 捕獲 self → 循環引用
    self.block = ^{
        [self doSomething];  // 隱式捕獲 self
    };
}
@end

解決方案:弱引用打破循環

- (void)setup {
    __weak typeof(self) weakSelf = self;  // 或 __weak MyClass *weakSelf = self;
    self.block = ^{
        __strong typeof(self) strongSelf = weakSelf;  // 升級為強引用,確保執行期間不被釋放
        if (strongSelf) {
            [strongSelf doSomething];
        }
    };
}

實戰最佳實踐模式

  1. 回調屬性必用 copy:@property (copy, nonatomic) void (^completion)(BOOL success);
  2. 異步回調捕獲 self 用弱引用:避免循環引用導致記憶體洩漏。
  3. Block 內修改外部變量加 __block:但謹慎使用,易造成邏輯混亂。
  4. 避免在 Block 內直接存取 self->ivar:會隱式捕獲 self,改用弱引用或傳遞所需值。
  5. GCD/NSOperation 併發 Block:注意線程安全,捕獲的變量若被多線程修改需加鎖或使用原子操作。

Block 內部結構(進階)

// 簡化的 Block 結構體
struct Block_layout {
    void *isa;           // 指向 _NSConcreteStackBlock / _NSConcreteMallocBlock / _NSConcreteGlobalBlock
    int flags;           // 位元標識:是否有 __block 變量、是否需要 copy/dispose 幫助函數
    int reserved;
    void (*invoke)(void *, ...);  // 執行函數指標
    struct Block_descriptor *descriptor; // 描述符:大小、copy/dispose 函數、簽名
    // 捕獲的變量緊隨其後...
};

理解 Block 的記憶體模型與捕獲語義,是寫出高效、無洩漏、無 Crash 的 Objective-C 代碼的關鍵。建議結合 Clang 靜態分析器、Instruments Leaks 工具、clang -rewrite-objc 查看 Block 編譯後的 C++ 代碼,深化理解。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
類型儲存位置生命週期觸發條件需手動管理
全域 Block資料區 (.data)程式全程不捕獲任何區域變量否
棧 Block堆疊定義作用域結束捕獲區域變量(預設)是 (需 copy 升級)
堆 Block堆引用計數歸零對棧 Block 執行 copy是 (ARC 自動管理)