Objective-C和Swift中的Block闭包详解

893 阅读3分钟

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. 常见用法

  • 尾随闭包:当闭包是最后一个参数时,可写在函数括号外。
  • 高阶函数mapfilter 等操作。
// 尾随闭包
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 BlockSwift Closure
语法^ 符号,显式声明类型{} 表达式,支持类型推断
变量捕获__block 修饰可修改变量默认捕获引用,需用捕获列表
内存管理ARC 下自动管理,属性用 copy自动引用计数,注意循环引用
循环引用处理__weak 打破强引用[weak self] 捕获列表
逃逸闭包默认可逃逸需标记 @escaping

应用场景

  • OC Block:GCD 任务、回调函数、封装代码块。
  • Swift Closure:函数式编程、异步回调、高阶函数操作。