Day4-Block 内存管理 & 循环引用分析
Block 内存管理 & 循环引用分析
1. Block 的结构体本质
Block 在底层其实是一个结构体:
1 | struct Block_literal { |
2. 捕获外部变量时,发生了什么?
变量类型 | 捕获方式 | 内存管理行为 |
---|---|---|
基本数据类型(int/float) | 值拷贝 | 不影响内存管理 |
Objective-C 对象 | 强引用(retain) | 需要注意循环引用 |
__block 修饰的对象 | 指针拷贝,但本身仍可能强引用 | 需配合 __weak __block |
3. Block 的 copy 和 dispose 函数
Block 如果捕获了对象,底层会自动生成 copy 和 dispose 函数:
- copy : retain 捕获的对象
- dispose : release 捕获的对象
简化示例:
1 | void _Block_copy(void *dst, void *src) { |
4. 深层次的循环引用分析
常见导致 Block 循环引用的场景:
- NSTimer / CADisplayLink / GCD
- 异步任务 (dispatch_async)
- 链式调用
标准防循环写法:
1 | __weak typeof(self) weakSelf = self; |
Block 高频面试问答
1. Block 的类型有哪些?分别什么时候出现?
答:
- Global Block:不捕获外部变量,存储在全局区。
- Stack Block:捕获了外部变量,存储在栈上。
- Malloc Block:拷贝自 Stack Block,存储在堆上。
ARC 下只存在 Global Block 和 Malloc Block。
2. Block 在 ARC 下是如何内存管理的?
答:
- ARC 会自动对 Block 执行 copy 操作,将其从栈拷贝到堆上。
- 使用 __strong 指针持有 Block 时,默认是堆 Block。
3. 如何判断一个 Block 是否造成了循环引用?
答:
- 检查 Block 内是否使用了
self
。 - 检查
self
是否强引用了 Block(比如 Block 是self
的属性)。 - 使用 Instruments 的 Leaks 工具分析内存泄漏。
4. __block 修饰符在 ARC 和 MRC 下分别是什么行为?
答:
- MRC:
__block
允许修改变量,但不会自动管理引用计数。 - ARC:
__block
允许修改,但并不会弱化引用关系,需要自己处理弱引用。
5. Block 为什么容易引起内存泄漏?具体有哪些场景?
答:
因为 Block 捕获变量是强引用,特别是对象指针,如下场景常出问题:
- UIViewController 的 Block 属性内部引用了
self
- NSTimer、CADisplayLink、GCD 中的 Block
解决办法:
- 使用
__weak
- 定时器使用
weakProxy
- GCD 注意生命周期控制
小结
理解 Block 的内存特性、引用关系变化(stack → heap)、以及在 ARC 下的默认 copy 机制,是彻底掌握 iOS 内存管理的关键。