block 是 Objective-C 中对闭包(closure)的实现,它允许你将一段代码逻辑连同其上下文环境一起封装起来,并作为参数传递、存储或延迟执行。理解 block 的原理、本质以及使用注意事项对于编写高效、安全的 Objective-C 代码非常重要。
一、Block 的原理与本质
1. 原理
Block 在底层是一个 结构体(struct),其中包含:
函数指针(指向 block 的实现代码)
描述 block 类型和内存管理信息的字段(如 isa 指针,用于支持 Objective-C 对象行为)
捕获的外部变量(根据变量类型,可能是值拷贝或引用)
当 block 被创建时,编译器会自动将其转换为一个对象(在 ARC 下是
NSMallocBlock、NSStackBlock或NSGlobalBlock),并处理捕获变量的内存布局。
2. 本质
Block 本质上是一个 带有上下文环境的函数对象(function object / functor)。
它可以访问其定义作用域内的变量(即“捕获”变量),但默认情况下这些变量是 只读的副本。
二、__block 的作用
1. 作用
__block修饰符用于 允许 block 内部修改外部作用域中的变量。默认情况下,block 捕获的是变量的 值拷贝(immutable)。加上
__block后,变量会被包装在一个特殊的结构体中(称为“byref”结构),block 和外部作用域共享这个结构体,从而实现 可变共享。
2. 示例
__block int x = 10;
void (^blk)(void) = ^{
x = 20; // 合法:x 被 __block 修饰
};
blk();
NSLog(@"%d", x); // 输出 20
三、使用 __block 的注意点
内存管理(尤其在 MRC 下):
__block变量在 block 被 copy 到堆上时,也会被移到堆上。在 ARC 下通常无需手动管理,但在 MRC 下需注意释放。
循环引用风险:
如果
__block修饰的是一个对象(如__block id obj),且该 block 被 obj 持有,则可能造成 循环引用。此时应配合
__weak使用(见下文)。
不要滥用:
仅在确实需要修改外部变量时才使用
__block。
四、为什么 block 属性要用 copy?
Block 默认创建在 栈上(
NSStackBlock),生命周期受限于当前作用域。如果要将 block 作为属性保存(如赋值给实例变量),必须将其 复制到堆上(变成
NSMallocBlock),否则离开作用域后 block 会失效,导致 crash。使用
copy修饰符会自动调用Block_copy(),确保 block 被正确移到堆上。
@property (nonatomic, copy) void (^completionHandler)(void);
即使在 ARC 下,也必须使用
copy,因为strong不会触发 block 的堆拷贝(虽然某些编译器会优化,但规范写法是copy)。
五、Block 使用注意事项
避免循环引用(retain cycle):
Block 会强引用捕获的对象(包括 self)。
解决方法:使用
__weak或__unsafe_unretained弱引用 self。
__weak typeof(self) weakSelf = self; self.block = ^{ [weakSelf doSomething]; };不要在 block 中直接修改非
__block修饰的局部变量(编译报错)。多线程环境下注意线程安全:
如果多个 block 并发修改同一个
__block变量,需加锁。
避免在 block 中过早 return 导致资源未释放(尤其在异步回调中)。
六、修改 NSMutableArray 是否需要 __block?
不需要。
原因:
NSMutableArray是一个 对象,block 捕获的是它的 指针(引用),而不是内容的拷贝。虽然 block 默认不能修改指针本身(即不能让变量指向另一个数组),但可以 调用其方法修改内部状态(如
addObject:)。
NSMutableArray *array = [[NSMutableArray alloc] init];
void (^blk)(void) = ^{
[array addObject:@"item"]; // ✅ 合法!修改的是对象内容,不是指针
};
blk();
NSLog(@"%@", array); // 输出 ["item"]
✅ 所以:只有当你需要重新赋值变量本身(如
array = anotherArray)时,才需要__block。
❌ 如果只是调用方法修改对象内部状态,不需要__block。
总结
希望这能帮你彻底理解 block 的机制与最佳实践!