自动变量,也就是局部变量,默认情况下在Objective-C中是存储在栈上的。它们的生命周期仅限于所在的作用域,比如函数或方法内部。当函数执行完毕,这些变量就会被释放。而静态变量是用static关键字声明的,它们的生命周期是整个程序运行期间,即使函数调用结束,静态变量的值也会保留下来,下次进入函数时还能继续使用。
接下来,用户可能需要了解这两种变量在Block中的捕获方式有什么不同。根据之前的回答,自动变量在Block中会被捕获为const,也就是说不能在Block内部修改,除非使用__block修饰符。而静态变量则可以直接在Block内部修改,因为Block捕获的是变量的地址,而不是值。
以下是 自动变量(auto变量) 和 静态变量(static变量) 的核心区别,结合代码说明它们的特性和在 Block 中的行为:
1. 本质区别
特性 | 自动变量(auto变量) | 静态变量(static变量) |
---|---|---|
存储位置 | 栈内存 | 数据区(程序生命周期内存在) |
生命周期 | 函数/方法执行期间 | 程序运行期间(即使函数退出也保留值) |
初始化 | 默认随机值(需手动初始化) | 默认初始化为0(或指定值) |
Block捕获行为 | 值捕获(需__block 修饰才能修改) |
地址捕获(可直接修改) |
2. 代码示例
示例 1:自动变量(auto变量)
scss
void testAutoVariable() {
int autoVar = 10; // 自动变量(默认是auto,可省略)
static int staticVar = 10; // 静态变量
void (^block)(void) = ^{
// autoVar = 20; // 编译错误:无法修改捕获的自动变量
staticVar = 20; // 允许修改静态变量
NSLog(@"autoVar = %d, staticVar = %d", autoVar, staticVar);
};
autoVar = 30; // 修改自动变量的值(Block捕获的是autoVar的初始值)
block();
NSLog(@"After block: staticVar = %d", staticVar);
}
// 调用函数
testAutoVariable();
输出结果:
ini
autoVar = 10, staticVar = 20
After block: staticVar = 20
关键解释:
-
自动变量
autoVar
:- Block 捕获的是其初始值(
autoVar = 10
),且在 Block 内部不可直接修改。 - 函数内修改
autoVar = 30
不影响 Block 捕获的值。
- Block 捕获的是其初始值(
-
静态变量
staticVar
:- Block 捕获的是变量地址,可直接修改。
- 修改后的值在函数外仍保留(
staticVar
变为 20)。
示例 2:使用 __block
修饰自动变量
objc
scss
void testBlockModifier() {
__block int blockVar = 10; // 允许Block内修改
void (^block)(void) = ^{
blockVar = 50; // 直接修改
NSLog(@"blockVar = %d", blockVar);
};
blockVar = 30; // 修改会影响Block内的值(因为Block持有引用)
block();
}
// 调用函数
testBlockModifier();
输出结果:
ini
blockVar = 50
关键解释:
__block
将自动变量移动到堆区,Block 持有其引用。- 函数内修改
blockVar = 30
会影响 Block 内的值(最终被 Block 修改为 50)。
3. 面试高频问题
Q:为什么静态变量不需要 __block
修饰就能在 Block 内修改?
- 答案:Block 对静态变量捕获的是其内存地址(而非值拷贝),因此可以直接修改。静态变量的地址在程序运行期间固定。
Q:自动变量和静态变量的生命周期对 Block 有什么影响?
-
答案:
- 自动变量在函数退出后销毁,若 Block 被异步执行(如 GCD),访问已释放的自动变量会导致野指针。此时必须用
__block
或将其复制到堆区。 - 静态变量始终存在,无需担心生命周期问题。
- 自动变量在函数退出后销毁,若 Block 被异步执行(如 GCD),访问已释放的自动变量会导致野指针。此时必须用
总结
场景 | 自动变量 | 静态变量 |
---|---|---|
Block内修改 | 需 __block 修饰 |
直接修改 |
生命周期依赖 | 依赖函数执行期间 | 全局存在 |
内存管理 | 栈内存(默认)或堆内存(__block ) |
数据区(无需管理) |
通过代码示例和对比,可以清晰理解两者在 Block 中的行为差异。