iOS优秀开发文章

iOS - block - 访问静态, 全局变量

2019-04-12  本文已影响0人  felix6

[toc]

参考

block - 访问静态, 全局变量

block - 捕获基本类型

一般变量可以分为以下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存储域改为栈

上一篇 下一篇

猜你喜欢

热点阅读