Day3-Block 分类、变量捕获与作用域

YVTU

Block 分类、变量捕获与作用域

一、Block 的分类

Block 在 Objective-C 中,根据其存储位置和生命周期分为以下三类:

Block 类型 存储位置 特点说明
Global Block 全局区 不捕获外部变量
Stack Block 栈上 捕获外部变量,随栈帧销毁
Heap Block 堆上 捕获外部变量,Block_copy 后存在堆上

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
void (^block1)(void) = ^{
NSLog(@"Hello");
};
// Global Block

int a = 10;
void (^block2)(void) = ^{
NSLog(@"a = %d", a);
};
// Stack Block

void (^block3)(void) = [block2 copy];
// Heap Block

二、变量捕获(Captured Variables)

1. 默认捕获(值语义)

Block 默认以值方式拷贝局部变量,不可修改。

1
2
3
4
5
6
int a = 5;
void (^block)(void) = ^{
NSLog(@"a is %d", a);
};
a = 6;
block(); // 输出 a is 5

2. __block 修饰(引用语义)

允许在 Block 内部修改外部变量。

1
2
3
4
5
6
__block int a = 5;
void (^block)(void) = ^{
a = 10;
};
block();
NSLog(@"a is %d", a); // 输出 a is 10

3. 对象捕获(指针语义)

对象变量以指针形式捕获,会被强引用。

1
2
3
4
5
NSString *str = @"Hello";
void (^block)(void) = ^{
NSLog(@"%@", str);
};
// str 被 retain

三、作用域与内存管理

生命周期:

  • Stack Block 生命周期短,超出作用域会被销毁。
  • 需要异步使用时,必须将 Block copy 到堆上,变为 Heap Block
  • ARC 下自动进行 copy 操作;MRC 需手动调用 Block_copy

示例(ARC 异步安全):

1
2
3
4
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSLog(@"Block executed");
});
// ARC 自动 copy 到堆

四、Swift 中的闭包捕获

Swift 中闭包默认强引用捕获对象:

1
2
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 后存在于堆中