Block 分类、变量捕获与作用域
一、Block 的分类
Block 在 Objective-C 中,根据其存储位置和生命周期分为以下三类:
| Block 类型 | 存储位置 | 特点说明 | 
| Global Block | 全局区 | 不捕获外部变量 | 
| Stack Block | 栈上 | 捕获外部变量,随栈帧销毁 | 
| Heap Block | 堆上 | 捕获外部变量,Block_copy 后存在堆上 | 
示例:
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | void (^block1)(void) = ^{NSLog(@"Hello");
 };
 
 
 int a = 10;
 void (^block2)(void) = ^{
 NSLog(@"a = %d", a);
 };
 
 
 void (^block3)(void) = [block2 copy];
 
 
 | 
二、变量捕获(Captured Variables)
1. 默认捕获(值语义)
Block 默认以值方式拷贝局部变量,不可修改。
| 12
 3
 4
 5
 6
 
 | int a = 5;void (^block)(void) = ^{
 NSLog(@"a is %d", a);
 };
 a = 6;
 block();
 
 | 
2. __block 修饰(引用语义)
允许在 Block 内部修改外部变量。
| 12
 3
 4
 5
 6
 
 | __block int a = 5;void (^block)(void) = ^{
 a = 10;
 };
 block();
 NSLog(@"a is %d", a);
 
 | 
3. 对象捕获(指针语义)
对象变量以指针形式捕获,会被强引用。
| 12
 3
 4
 5
 
 | NSString *str = @"Hello";void (^block)(void) = ^{
 NSLog(@"%@", str);
 };
 
 
 | 
三、作用域与内存管理
生命周期:
- Stack Block 生命周期短,超出作用域会被销毁。
- 需要异步使用时,必须将 Block copy 到堆上,变为 Heap Block。
- ARC 下自动进行 copy操作;MRC 需手动调用Block_copy。
示例(ARC 异步安全):
| 12
 3
 4
 
 | dispatch_async(dispatch_get_global_queue(0, 0), ^{NSLog(@"Block executed");
 });
 
 
 | 
四、Swift 中的闭包捕获
Swift 中闭包默认强引用捕获对象:
| 12
 3
 4
 5
 6
 7
 8
 
 | class MyClass {var name = "Test"
 func doSomething() {
 DispatchQueue.global().async { [weak self] in
 print(self?.name ?? "nil")
 }
 }
 }
 
 | 
使用 [weak self] / [unowned self] 可避免循环引用。
五、小结对比表
| 分类 | 是否捕获变量 | 存储位置 | 生命周期控制 | 
| Global Block | 否 | 全局区 | 程序生命周期内常驻 | 
| Stack Block | 是 | 栈上 | 随栈帧销毁 | 
| Heap Block | 是 | 堆上 | Block_copy 后存在于堆中 |