[toc]
参考
一般变量可以分为以下5种: 自动变量(局部变量)、静态局部变量、全局变量、静态全局变量、函数参数( 也是局部变量 )。
oc代码
block 访问: 全局变量, 静态全局变量, 静态局部变量, 局部变量
NSInteger global_val = 1; // 全局变量
static NSInteger global_static_val = 1; // 静态全局变量
int main(int argc, const char * argv[]) {
@autoreleasepool {
static NSInteger static_val = 1; // 静态局部变量
NSInteger auto_val = 1; // 局部变量
NSLog(@"0: %ld - %ld - %ld - %ld ~ %p - %p - %p - %p", global_val, global_static_val, static_val, auto_val, &global_val, &global_static_val, &static_val, &auto_val);
// 定义block
void (^myBlock)(void) = ^{
global_val ++;
global_static_val ++;
static_val ++;
NSLog(@"1: %ld - %ld - %ld - %ld ~ %p - %p - %p - %p", global_val, global_static_val, static_val, auto_val, &global_val, &global_static_val, &static_val, &auto_val);
};
global_val ++;
global_static_val ++;
static_val ++;
auto_val ++;
NSLog(@"2: %ld - %ld - %ld - %ld ~ %p - %p - %p - %p", global_val, global_static_val, static_val, auto_val, &global_val, &global_static_val, &static_val, &auto_val);
// 调用block
myBlock();
NSLog(@"3: %ld - %ld - %ld - %ld ~ %p - %p - %p - %p", global_val, global_static_val, static_val, auto_val, &global_val, &global_static_val, &static_val, &auto_val);
}
return 0;
}
MRC 输出: (全局 - 静态全局 - 静态局部 - 局部)
0: 1 - 1 - 1 - 1 ~ 0x100001290 - 0x1000012a0 - 0x100001298 - 0x7ffeefbff408
2: 2 - 2 - 2 - 2 ~ 0x100001290 - 0x1000012a0 - 0x100001298 - 0x7ffeefbff408
1: 3 - 3 - 3 - 1 ~ 0x100001290 - 0x1000012a0 - 0x100001298 - 0x7ffeefbff3f8 // 局部变量被捕获, 在新的栈地址 ★★
3: 3 - 3 - 3 - 2 ~ 0x100001290 - 0x1000012a0 - 0x100001298 - 0x7ffeefbff408
ARC 输出:
0: 1 - 1 - 1 - 1 ~ 0x1000022c0 - 0x1000022d0 - 0x1000022c8 - 0x7ffeefbff408
2: 2 - 2 - 2 - 2 ~ 0x1000022c0 - 0x1000022d0 - 0x1000022c8 - 0x7ffeefbff408
1: 3 - 3 - 3 - 1 ~ 0x1000022c0 - 0x1000022d0 - 0x1000022c8 - 0x1005839f0 // 局部变量随block拷贝到堆 ★★
3: 3 - 3 - 3 - 2 ~ 0x1000022c0 - 0x1000022d0 - 0x1000022c8 - 0x7ffeefbff408
c++代码分析
NSInteger global_val = 1;
static NSInteger global_static_val = 1;
int main(int argc, const char * argv[]) {
{ __AtAutoreleasePool __autoreleasepool; /* @autoreleasepool */
static NSInteger static_val = 1;
NSInteger auto_val = 1;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_p5_mp3284bs2xb073r91w__n99r0000gn_T_main_3aec79_mi_0, global_val, global_static_val, static_val, auto_val, &global_val, &global_static_val, &static_val, &auto_val);
void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &static_val, auto_val));
global_val ++;
global_static_val ++;
static_val ++;
auto_val ++;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_p5_mp3284bs2xb073r91w__n99r0000gn_T_main_3aec79_mi_2, global_val, global_static_val, static_val, auto_val, &global_val, &global_static_val, &static_val, &auto_val);
((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
NSLog((NSString *)&__NSConstantStringImpl__var_folders_p5_mp3284bs2xb073r91w__n99r0000gn_T_main_3aec79_mi_3, global_val, global_static_val, static_val, auto_val, &global_val, &global_static_val, &static_val, &auto_val);
}
return 0;
}
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
NSInteger *static_val; // 捕获到的静态局部变量指针, 指针传递, 引用了变量地址, 通过该指针就能修改内存, 从而修改block外部定义的static变量
NSInteger auto_val; // 捕获到的普通局部变量(副本); 捕获的是局部变量val的★瞬时值★, 相当于按值传递, 进行了一次只读拷贝
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, NSInteger *_static_val, NSInteger _auto_val, int flags=0) : static_val(_static_val), auto_val(_auto_val) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
NSInteger *static_val = __cself->static_val; // bound by copy 该函数通过传入的结构体指针访问捕获到的成员变量
NSInteger auto_val = __cself->auto_val; // bound by copy
global_val ++;
global_static_val ++;
(*static_val) ++;
NSLog((NSString *)&__NSConstantStringImpl__var_folders_p5_mp3284bs2xb073r91w__n99r0000gn_T_main_3aec79_mi_1, global_val, global_static_val, (*static_val), auto_val, &global_val, &global_static_val, &(*static_val), &auto_val);
}
可以看到, 系统自动加上的注释 "bound by copy
", 未使用 __block
时, 自动变量auto_val
虽然被捕获进来了, 但是是用 __cself->auto_val
来访问的。
block 仅仅捕获了auto_val
的值, 并没有捕获auto_val
的内存地址。
所以即便在 __main_block_func_0
这个函数中即使重写自动变量auto_val
的值, 依旧没法去改变block外面自动变量auto_val
的值。
OC 可能是基于这一点, 在编译层面就防止开发者可能犯的错误, 因为自动变量没法在 block 中改变外部变量的值, 所以编译过程中就报编译错误。
注意: desc这里没有 copy 和 dispose 函数
static struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
} __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0) };
结论
block 只捕获 block 中用到的变量。
block 访问 (静态)全局变量, 无需捕获, 直接访问/修改 (因为是全局的, 作用域很广)。
block 访问 静态局部变量, 变量地址会被捕获成为 block 结构体 __main_block_impl_0
的成员, 引用变量地址, 指针传递, block 包内访问的都是变量地址, 直接进行读写操作。
block 访问 普通局部变量, 变量的值会被捕获成为block结构体 __main_block_impl_0
的成员, 引用其瞬时值, 按值传递, block 包内不允许修改其值(否则报编译错误), 这里所说的值, 指的是栈中指针所指向的地址。
也因此普通局部变量在block外部修改, 新值不会被 block 捕获。
表格
变量类型 | 是否被捕获 | 访问方式 | 默认包内能否修改 | 访问后果 |
---|---|---|---|---|
(静态)全局变量 | 否 | 直接访问 | 能 | 无 |
静态局部变量 | 是 (捕获指针) | 指针传递 | 能 | 无 |
局部变量 | 是 (捕获瞬时值) | 值传递 | 否 | block存储域改为栈 |
网友评论