一、Block的三种类型与内存区域
| Block类型 | 内存区域 | 触发条件 | 内存管理 |
|---|---|---|---|
__NSGlobalBlock__ | 全局区 | Block未捕获外部变量 | 无需管理,类似全局函数 |
__NSStackBlock__ | 栈区 | 非ARC环境下捕获外部变量,未执行copy操作 | 函数返回后自动释放 |
__NSMallocBlock__ | 堆区 | 捕获外部变量且执行copy操作(ARC自动处理) | 需手动管理(MRC)或自动管理(ARC) |
二、内存管理核心规则
1. 变量捕获规则
-
基本数据类型:
- 不加
__block修饰:Block内部值拷贝,无内存管理问题 - 加
__block修饰:Block内部指针引用,需处理生命周期
- 不加
-
对象类型:
- Block默认对对象强引用(ARC/MRC均可能引起循环引用)
__block修饰符在MRC下可避免强引用,但在ARC下无效
2. 内存管理操作
- MRC环境:
// 必须手动copy到堆 void (^block)(void) = [^{ NSLog(@"MRC Block"); } copy]; [block release]; // 使用后需释放 - ARC环境:
- 大多数情况自动将Block从栈复制到堆
- 例外情况:将Block赋值给
strong/copy修饰的属性时自动执行copy
三、循环引用与解决方案
1. 典型场景
// self -> block -> self 形成循环引用
self.block = ^{
[self doSomething];
};
2. 解决方案
-
弱引用 + 强引用安全模式:
__weak typeof(self) weakSelf = self; self.block = ^{ __strong typeof(weakSelf) strongSelf = weakSelf; [strongSelf doSomething]; }; -
手动打破循环(MRC):
// 使用__block修饰符避免强引用 __block typeof(self) blockSelf = self; self.block = ^{ [blockSelf doSomething]; blockSelf = nil; // 执行后手动置nil };
四、关键实践建议
-
ARC环境下优先使用:
@property (copy, nonatomic) void (^block)(void); // 必须用copy修饰 -
避免直接访问实例变量:
// 错误写法(隐式self引用) ^{ _name = @"Tom"; }; // 正确写法(显式weakSelf) ^{ weakSelf.name = @"Tom"; }; -
特殊变量处理:
// 静态变量无需内存管理 static int count = 0; ^{ count++; };
五、调试技巧
-
检测Block类型:
NSLog(@"Block类型: %@", [block class]); // 输出__NSGlobalBlock__等 -
内存泄漏检测工具:
- Xcode Memory Graph Debugger
- Instruments的
Leaks工具
附:MRC与ARC对比
| 操作 | MRC | ARC |
|---|---|---|
| Block赋值 | 必须显式copy | 自动copy(strong/copy属性) |
__block修饰对象 | 不增加引用计数 | 仍强引用(需配合__weak) |
| 释放时机 | 手动release | 自动释放(引用计数为0时) |