美文网首页iOS优秀开发文章
iOS - block - 访问静态, 全局变量

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

作者: felix6 | 来源:发表于2019-04-12 17:32 被阅读0次

    [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存储域改为栈

    相关文章

      网友评论

        本文标题:iOS - block - 访问静态, 全局变量

        本文链接:https://www.haomeiwen.com/subject/arhhwqtx.html