Objective-c Block 面试题
以下是对我们这整段关于 Objective-C 中 Block、__block
修饰符、内存管理行为、生命周期等内容的全面总结,并附带了一套适合面试准备的面试题集(带答案)。
🧠 一、知识总结:Objective-C Block + __block 修饰符
✅ Block 的三种类型
Block 类型 | 类名(runtime) | 存储位置 | 是否捕获变量 | 生命周期 |
---|---|---|---|---|
全局 Block | __NSGlobalBlock__ | 全局区(.data) | ❌ 不捕获 | 程序整个生命周期 |
栈 Block | __NSStackBlock__ | 栈上 | ✅ 捕获 | 随函数作用域结束销毁 |
堆 Block | __NSMallocBlock__ | 堆上(copy 后) | ✅ 捕获 | ARC/MRC 管理 |
✅ ARC 与 MRC 的区别
特性 | ARC | MRC |
---|---|---|
是否自动 copy Block | ✅ 是(捕获变量会自动 copy 到堆) | ❌ 否(需手动 copy) |
NSAutoreleasePool 可用? | ❌ 不可用,改用 @autoreleasepool | ✅ 可以使用 |
retain/release 是否可用? | ❌ 禁止 | ✅ 手动管理 |
✅ __block
修饰符
特性 | 默认变量 | __block 修饰 |
---|---|---|
是否可在 Block 中修改 | ❌ 否(值捕获) | ✅ 是(引用捕获) |
捕获方式 | 值拷贝(by value) | 引用封装为结构体 |
生命周期 | Block 生命周期内有效 | Block 生命周期或外部引用共同决定 |
释放机制 | 不需要释放(副本) | 自动随 Block 释放;ARC 下自动 retain/release |
✅ 修改变量是否需要 __block
操作类型 | 是否需要 __block |
---|---|
修改对象内容(如 addObject: ) | ❌ 不需要 |
修改指针变量本身(如 obj = nil ) | ✅ 需要 |
修改基本类型变量 | ✅ 需要 |
捕获但不修改变量 | ❌ 不需要 |
✅ 生命周期补充
捕获变量类型 | 生命周期默认与 Block 相同? | 可否更长? |
---|---|---|
基本类型 | ✅ 是 | ❌ 否 |
对象类型(未被强引用) | ✅ 是 | ✅ 可被外部引用延长 |
__block 捕获对象 | ✅ 是 | ✅ 可 retain 延长或造成循环引用 |
📚 二、面试题集
以下是整理出的 15 道典型面试题(附答案),覆盖基础、陷阱和进阶内容:
✅ 基础题
Q1: Block 有哪三种类型?它们的区别是什么?
🅰️:__NSGlobalBlock__
(不捕获变量,放在全局区)、__NSStackBlock__
(捕获变量,栈上,临时)、__NSMallocBlock__
(捕获变量 + copy 后的堆 Block,生命周期更长)。
Q2: 什么情况下 Block 是 __NSGlobalBlock__
?
🅰️:当 Block 没有捕获任何外部变量时,它就是 __NSGlobalBlock__
,存在全局数据段,程序整个生命周期都有效。
Q3: 在 ARC 和 MRC 下 Block 的默认行为有何不同?
🅰️:
- ARC:Block 捕获变量时自动拷贝到堆上(自动成为
__NSMallocBlock__
) - MRC:Block 默认在栈上(
__NSStackBlock__
),需手动 copy。
Q4: 为什么要将 Block 从栈 copy 到堆?
🅰️:因为栈上的 Block 随函数调用结束而销毁,拷贝到堆上可以安全地在异步、延迟或跨作用域中使用。
Q5: ARC 下是否需要手动调用 copy
?
🅰️:不需要,编译器会自动为你 copy
Block 到堆上。
✅ 进阶题
Q6: __block
关键字的作用是什么?
🅰️:允许 Block 修改变量本身(不是对象内部属性),它会将变量封装成一个结构体,以引用方式捕获。
Q7: 为什么修改变量需要 __block
?
🅰️:因为 Block 默认对基本变量是值拷贝,不允许修改。__block
改为引用方式封装,可被 Block 修改。
Q8: Block 中能修改数组吗?需要 __block
吗?
🅰️:可以修改数组内容,不需要 __block
;但如果要让数组指针指向其他对象,则需要 __block
。
Q9: __block
修饰的对象何时释放?
🅰️:与 Block 生命周期一致,Block 销毁时会释放其引用结构体;对象本身在 ARC 下自动 retain/release。
Q10: Block 会强引用捕获的对象吗?
🅰️:是的,Block 默认会 retain 被捕获的对象 ➜ 导致循环引用风险。
✅ 高阶陷阱题
Q11: Block 内部使用 self
会出现什么问题?如何解决?
🅰️:会强引用 self
,导致循环引用。解决方式是用 __weak
修饰 self
的弱引用。
Q12: 在 MRC 中,为什么 NSAutoreleasePool
必须与 autorelease
配合使用?
🅰️:因为 autorelease
注册的对象会在最近的 pool 被 drain
时释放,pool 负责释放“延迟对象”。
Q13: 是否有办法让 Block 捕获的对象比 Block 更长寿命?
🅰️:可以。如果外部对对象有强引用(如全局变量、单例、property strong),对象就会比 Block 活得更久。
Q14: Block 是否可能与 __block
变量形成 retain cycle?
🅰️:是的。例如 obj.block = ^{ NSLog(@"%@", obj); };
就形成了互相引用,导致内存泄漏。
Q15: 如何破除 Block 的循环引用?
🅰️:使用 __weak
或 __unsafe_unretained
修饰外部对象,避免 Block 内 retain 它。