(三)Block之截获变量和对象

96
madaoCN
2017.10.21 19:28* 字数 440

相关文章


捕获自动变量

int i = 0;//自动变量
void (^blk)() = ^() {
    printf("%d", i);
};
blk();

经过转换代码后的部分源代码

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  int i; //需要注意
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int _i, int flags=0) : i(_i) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};
// Block方法指针
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
        int i = __cself->i; // bound by copy 需要注意
        printf("%d", i);
 }
// 构造函数的调用
__main_block_impl_0 blk = __main_block_impl_0(
                            __main_block_func_0, 
                            &__main_block_desc_0_DATA, 
                            i);

可见,自动变量i被追加到了__main_block_impl_0结构体中(如果Block语法中没有使用自动变量,则不会被追加)
在构造函数调用时候自动变量i也被当做参数传入了构造方法中
那么可知__main_block_impl_0被初始化为如下:

impl.isa = &_NSConcreteStackBlock;
impl.Flags = 0;
impl.FuncPtr = __main_block_func_0;
Desc = & __main_block_desc_0_DATA;
i = 0;

然后,将关注点放在Block方法指针上

 int i = __cself->i; 

执行Block语法时候,将__main_block_impl_0捕获的自动变量,被赋值给了内部新定义的变量

总的来说 “截获自动变量”意味着 将即将使用的变量保存到Block结构体实例中

捕获对象

id array = [[NSMutableArray alloc] init];
void (^blk)() = ^() {
     [array addObject:@"obj"];
     printf("%lu", (unsigned long)[array count]);
};
blk();

经过转换代码后的部分源代码

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  __strong id array; //注意这里
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __strong id _array, int flags=0) : array(_array) {
    impl.isa = &_NSConcreteStackBlock;
    impl.Flags = flags;
    impl.FuncPtr = fp;
    Desc = desc;
  }
};

static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
        __strong id array = __cself->array; // bound by copy
       [array addObject:@"obj"];
       printf("%lu", (unsigned long)[array count]);
}

static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) 
{
        _Block_object_assign((void*)&dst->array, 
                                     (void*)src->array,       
                                     3/*BLOCK_FIELD_IS_OBJECT*/);
}

static void __main_block_dispose_0(struct __main_block_impl_0*src)
 {  
        _Block_object_dispose((void*)src->array, 
                               3/*BLOCK_FIELD_IS_OBJECT*/);
}

static struct __main_block_desc_0 {
  size_t reserved;
  size_t Block_size;
  void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);//注意这里
  void (*dispose)(struct __main_block_impl_0*);//注意这里
} 
__main_block_desc_0_DATA =
 {
    0,
    sizeof(struct __main_block_impl_0), 
    __main_block_copy_0, 
    __main_block_dispose_0
};
  • __main_block_impl_0内部以__strong id array;形式捕获了对象
    那我们知道,c语言在Object中是不支持__strong修饰符的,那么是如何对其内存管理的呢?
    我们注意到,转换后的代码多出了两个方法__main_block_copy_0__main_block_dispose_0
    前者于OC的retain方法 ,将捕获的对象赋值给另一个对象结构体的的成员变量中
    后者相当于 release方法,将结构体实例中捕获的变量释放

  • 内存管理方法调用时机

函数 调用时机
__main_block_copy_0 Block从栈复制到堆
__main_block_dispose_0 堆上Block被废弃

那么什么时候栈会被复制到堆呢?

  • 调用copy方法
  • Block作为返回值(超出作用域)
  • 将Block赋值给id 或者 __strong类型变量
  • 在带有usingBlock的Cocoa方法或者GCD的API中传递Block时候
IOS那些事儿
Web note ad 1