Objective-C 中的 Block 和 Swift 中的 Closure 详解
一、Objective-C 中的 Block
1. 基本语法
Block 是 Objective-C 中闭包的实现,语法为 返回值类型 (^Block名称)(参数列表) = ^(参数列表) { ... }。
// 无参无返回值
void (^simpleBlock)(void) = ^{
NSLog(@"This is a simple block");
};
simpleBlock();
// 带参数和返回值
int (^addBlock)(int, int) = ^(int a, int b) {
return a + b;
};
int result = addBlock(3, 5); // result = 8
2. 变量捕获
默认捕获外部变量为 const 副本,需用 __block 修饰变量以允许修改。
__block int counter = 0;
void (^incrementBlock)(void) = ^{
counter++;
NSLog(@"Counter: %d", counter);
};
incrementBlock(); // 输出 Counter: 1
3. 内存管理
- MRC 下:Block 默认在栈上,需用
copy移至堆以延长生命周期。 - ARC 下:编译器自动管理,但作为属性时仍需用
copy。 - 循环引用:使用
__weak打破强引用。
// 循环引用示例及解决
@interface MyClass : NSObject
@property (nonatomic, copy) void (^myBlock)(void);
@end
@implementation MyClass
- (void)setupBlock {
__weak typeof(self) weakSelf = self;
self.myBlock = ^{
NSLog(@"%@", weakSelf); // 使用 weak 避免循环引用
};
}
@end
4. 常见用法
- GCD:异步任务封装。
- 回调函数:作为方法参数传递。
// 作为方法参数
- (void)doTaskWithCompletion:(void (^)(BOOL))completion {
// 执行任务...
completion(YES);
}
// 调用
[self doTaskWithCompletion:^(BOOL success) {
NSLog(@"Task completed: %d", success);
}];
二、Swift 中的 Closure
1. 基本语法
闭包表达式:{ (参数) -> 返回值类型 in 代码 },支持类型推断和简写。
// 完整写法
let add: (Int, Int) -> Int = { (a: Int, b: Int) -> Int in
return a + b
}
// 简写(类型推断)
let add = { a, b in a + b }
print(add(3, 5)) // 输出 8
2. 值捕获
闭包通过引用捕获变量,需用捕获列表避免循环引用。
var counter = 0
let increment = {
counter += 1
print("Counter: (counter)")
}
increment() // 输出 Counter: 1
3. 逃逸与非逃逸闭包
@escaping:闭包在函数返回后执行(如异步回调)。- 默认非逃逸:闭包在函数体内执行完毕。
// 逃逸闭包
func fetchData(completion: @escaping (String) -> Void) {
DispatchQueue.global().async {
completion("Data received")
}
}
4. 自动闭包
@autoclosure 将表达式包装为闭包,延迟求值。
func logIfTrue(_ condition: @autoclosure () -> Bool) {
if condition() {
print("True")
}
}
logIfTrue(2 > 1) // 输出 True
5. 常见用法
- 尾随闭包:当闭包是最后一个参数时,可写在函数括号外。
- 高阶函数:
map、filter等操作。
// 尾随闭包
let numbers = [1, 2, 3]
let doubled = numbers.map { $0 * 2 } // [2, 4, 6]
// 捕获列表避免循环引用
class MyClass {
var closure: (() -> Void)?
func setupClosure() {
closure = { [weak self] in
print(self?.description ?? "Self is nil")
}
}
}
三、内部原理
1. Objective-C Block
-
内存结构:Block 是栈上的对象,包含函数指针和捕获变量副本。
__block变量会被封装为结构体,实现跨 Block 修改。 -
类型:
NSGlobalBlock:未捕获外部变量,存储在全局区。NSStackBlock:默认在栈上,生命周期随作用域结束。NSMallocBlock:调用copy后移至堆,供跨作用域使用。
-
循环引用:Block 强引用其捕获的对象,需用
__weak打破。
2. Swift Closure
- 引用类型:闭包是引用类型,赋值时共享同一上下文。
- 内存管理:捕获的变量通过引用捕获,可能导致循环引用,需用捕获列表
[weak self]或[unowned self]。 - 上下文保存:闭包在堆上分配内存,存储捕获变量和函数地址,非逃逸闭包可能优化为栈分配。
四、总结
| 特性 | Objective-C Block | Swift Closure |
|---|---|---|
| 语法 | ^ 符号,显式声明类型 | {} 表达式,支持类型推断 |
| 变量捕获 | __block 修饰可修改变量 | 默认捕获引用,需用捕获列表 |
| 内存管理 | ARC 下自动管理,属性用 copy | 自动引用计数,注意循环引用 |
| 循环引用处理 | __weak 打破强引用 | [weak self] 捕获列表 |
| 逃逸闭包 | 默认可逃逸 | 需标记 @escaping |
应用场景:
- OC Block:GCD 任务、回调函数、封装代码块。
- Swift Closure:函数式编程、异步回调、高阶函数操作。