美文网首页
《Objective-C高级编程 iOS与OS X多线程与内存管

《Objective-C高级编程 iOS与OS X多线程与内存管

作者: 我才是臭吉吉 | 来源:发表于2019-01-12 18:36 被阅读12次

    Blocks篇:3.Blocks使用捕获到的变量

    所谓Blocks捕获变量,即在Block函数体内使用外部声明的变量。

    1. 捕获局部变量(自动变量)和静态局部变量

    // main.m
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            int uselessVal = 1;
            // 局部变量
            int val = 3;
            // 静态局部变量
            static int staticVal = 4;
            
            void (^myBlock)(void) = ^{
                // 捕获val和staticVal使用
                printf("val = %d\n", val);
                printf("staticVal = %d\n", staticVal);
            };
            myBlock();
        }
        return 0;
    }
    

    转换后的C++代码部分如下:

    // main.cpp
    
    /** Block的完整结构体声明 */
    struct __main_block_impl_0 {
        struct __block_impl impl;
        struct __main_block_desc_0* Desc;
        int val; // 内部捕获的局部变量
        int *staticVal; // 内部捕获的静态局部变量
        __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _val, int *_staticVal, int flags=0) : val(_val), staticVal(_staticVal) {
            impl.isa = &_NSConcreteStackBlock; // 这里只按照MRC编译确定,ARC则不同
            impl.Flags = flags;
            impl.FuncPtr = fp;
            Desc = desc;
        }
    };
    
    /** Block中函数体转换生成的C函数 */
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
        // 获取Block结构体实例中捕获的变量
        int val = __cself->val; // bound by copy
        int *staticVal = __cself->staticVal; // bound by copy
        printf("val = %d\n", val);
        printf("staticVal = %d\n", (*staticVal));
    }
    
    
    // main函数
    int main(int argc, const char * argv[]) {
        /* @autoreleasepool */ { __AtAutoreleasePool __autoreleasepool; 
            int uselessVal = 1;
            int val = 3;
            static int staticVal = 4;
    
            void (*myBlock)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, val, &staticVal)); // 初始化时赋值,将要捕获的变量传递进去
            ((void (*)(__block_impl *))((__block_impl *)myBlock)->FuncPtr)((__block_impl *)myBlock);
        }
        return 0;
    }
    

    以上可以看出:

    1. 只有Block内部使用的变量才会被捕获;
    2. 普通的局部变量是通过值传递方式进行捕获,静态局部变量是通过指针传递方式进行的;
      • 原因:
        1. 由于C函数的声明和实现是在原局部变量的生命周期之外(这里是main函数外部),使用时原变量早已被释放,故需要直接将值保存到Block结构体实例中;
        2. 对于静态局部变量,由于静态变量是存储在专门的数据区(也就可以理解为全局),在C函数中也可以直接访问,故只需将其指针保存在Block结构体实例中即可。
    3. C函数内部使用时,通过传递进去的Block指针取出被捕获的变量值。
    4. 补充:在ARC环境下,对于Block的类型(isa)
      • 捕获普通局部变量后,在运行时,系统会将Block拷贝到堆上,变为_NSConcreteMallocBlock
      • 捕获静态局部变量,虽然使用,实际也只是记录该变量的指针,Block为_NSConcreteGlobalBlock
      • 没有捕获任何变量的Block为_NSConcreteGlobalBlock类型

    2. 捕获全局变量和全局静态变量

    // main.m
    
    int globalVal = 3;
    static int globalStaticVal = 4;
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            void (^myBlock)(void) = ^{
                printf("globalVal = %d\n", globalVal);
                printf("globalStaticVal = %d\n", globalStaticVal);
            };
            myBlock();
        }
        return 0;
    }
    

    转换后的C++部分代码为:

    // main.cpp
    
    int globalVal = 3;
    static int globalStaticVal = 4;
    
    /** Block完整结构体声明 */
    struct __main_block_impl_0 {
        // 没有保存全局变量
        struct __block_impl impl;
        struct __main_block_desc_0* Desc;
        __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) {
            impl.isa = &_NSConcreteStackBlock;
            impl.Flags = flags;
            impl.FuncPtr = fp;
            Desc = desc;
      }
    };
    
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
        // 直接使用
        printf("globalVal = %d\n", globalVal);
        printf("globalStaticVal = %d\n", globalStaticVal);
    }
    

    可以看到,

    1. 对于全局变量(包含静态全局变量),由于在Block的C函数中可以直接访问,故无需在Block结构中对其进行额外的保存工作
    2. 在ARC环境下,Block的类型(isa)为_NSConcreteGlobalBlock

    相关文章

      网友评论

          本文标题:《Objective-C高级编程 iOS与OS X多线程与内存管

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