zoukankan      html  css  js  c++  java
  • Block中为什么无法引用c语言数组

    更新记录

    时间 版本修改
    2020年4月12日 初版

    Block引用C语言数组报错

    char text[] = "hello";
    void (^blk)(void) = ^{
            printf("%c
    ",text[2]);
    };
    
    • 报编译错误:error:cannot refer to declaration with an array type inside block
    • 《Objective-C 高级编程 iOS与OS X多线程和内存管理》2.3.1 Block的实质中,提出解决方案,修改成如下代码:
    char text[] = "hello";
    int main(int argc, const char * argv[]) {
       void (^blk)(void) = ^{
            printf("%c
    ",text[2]);
        };
        return 0;
    }
    

    报错的根本原因

    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      const char *fmt;
      int val;              //block捕获的变量 val
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _val, int flags=0) : fmt(_fmt), val(_val) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    • 上述的block,捕获了一个const char*和一个int。所以构造函数中会多两个参数。
    • 上述就是目前编译器的实现,所以如果捕获一个C语言数组,会变成如下代码:
    struct __main_block_impl_0 {
      struct __block_impl impl;
      struct __main_block_desc_0* Desc;
      const char *fmt;
      int val;              //block捕获的变量 val
      char szArray[10];
      __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, const char *_fmt, int _val, char _szArray[10], int flags=0) : fmt(_fmt), val(_val), szArray[10](_szArray) {
        impl.isa = &_NSConcreteStackBlock;
        impl.Flags = flags;
        impl.FuncPtr = fp;
        Desc = desc;
      }
    };
    
    • 由于转换成了构造函数的参数列表的形式,实质上就是调用了szArray[10] = _szArray这样的语句。虽然变量的类型以及数组的大小都相同,但C语言规范不允许这种赋值。因此会报编译错误。

    捕获指针替代捕获C语言数组

    • 上述可以正常编译的代码,实际对应的转换后的C/C++源代码如下:
    struct __block_impl {
        void *isa;
        int Flags;
        int Reserved;
        void *FuncPtr;
    };
    struct __main_block_impl_0 {
        struct __block_impl impl;
        struct __main_block_desc_0* Desc;
        char *text;
        __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, char *_text, int flags=0) : text(_text) {
            impl.isa = &_NSConcreteStackBlock;
            impl.Flags = flags;
            impl.FuncPtr = fp;
            Desc = desc;
        }
    };
    static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
        char *text = __cself->text; // bound by copy
    
        printf("%c
    ",text[2]);
        
    }
    
    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)};
    
    int main(int argc, const char * argv[]) {
        char text[] = "hello";
        __main_block_impl_0 block_struct = __main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, text);
        void (*blk)() = (void (*)())&block_struct;
        ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk);
        return 0;
    }
    
    • 因为对于指针的赋值text = _text是符合C语言语法规范的,因此这种用法是没有任何问题的。

    阅读延申

    char text[] = "hello";
    int main(int argc, const char * argv[]) {
       void (^blk)(void) = ^{
            printf("%c
    ",text[2]);
        };
        return 0;
    }
    
    • 但是,它的原因是因为:这个Block对象里面使用的是全局变量,它是不需要存储到自己的Block结构体中,即它的结构体应该是如下结构:
    struct __main_block_impl_0 {
        struct __block_impl impl;
        struct __main_block_desc_0* Desc;
    
        //如果block截获了自动变量,会放置在这里。由于该block内引用的是全局变量,并不会在此加入字段进行初始化。
    
        __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, char *_text, int flags=0) : text(_text) {
            impl.isa = &_NSConcreteStackBlock;
            impl.Flags = flags;
            impl.FuncPtr = fp;
            Desc = desc;
        }
    };
    
    • 虽然博文的结果具有参考性,但是需要特别区分好这两种做法的底层特性。

    参考资料

    • 《Objective-C 高级编程 iOS与OS X多线程和内存管理》2.3.1 Block的实质
    • 《Objective-C 高级编程 iOS与OS X多线程和内存管理》2.3.2 截获自动变量值
  • 相关阅读:
    redis 切换大量的缓存数据
    springboot jdbctemplate 常用的语法
    Spring Boot 整合 jdbctemplate 多数据源
    Spring Boot 整合 jdbctemplate 单数据源
    IDEA(Eclipse) 常用的快捷键(快速开发)
    bigdecimal 类型的变量怎么相互加减乘除
    在js和java中判断手机访问的是ios系统还是android系统
    fiddler抓web请求
    sign和token设计
    移动端自动化测试-Windows-Android-Appium环境搭建
  • 原文地址:https://www.cnblogs.com/HelloGreen/p/12688875.html
Copyright © 2011-2022 走看看