美文网首页
Block笔记(五)

Block笔记(五)

作者: MichealXXX | 来源:发表于2018-10-29 21:05 被阅读0次
截获对象

这一篇我们来看一下Block截获对象

{
  id array = [[NSMutableArray alloc] init];
}

id类型的变量相当于默认附有__strong修饰符超出作用域对象会被立即释放并且废弃,我们来看一下以下源码。

typedef void(^blk_t)(id);

int main(int argc, char * argv[]) {
    
    
    blk_t blk;
    {
        id array = [[NSMutableArray alloc] init];
        
        blk = [^(id obj){
            [array addObject:obj];
            NSLog(@"Now the count of array = %ld", (long)[array count]);
        } copy];
    }
    
    blk([[NSString alloc] init]);
    blk([[NSString alloc] init]);
    blk([[NSString alloc] init]);
    
}

变量作用域结束的同时,变量array被废弃,强引用失效,因此赋值给变量arrayNSMutableArray类的对象必定被释放并废弃。但是源码运行正常,执行结果如下:

2018-10-28 16:32:55.885619+0800 test[2282:245454] Now the count of array = 1
2018-10-28 16:32:55.887405+0800 test[2282:245454] Now the count of array = 2
2018-10-28 16:32:55.887618+0800 test[2282:245454] Now the count of array = 3

这一结果意味着赋值给变量arrayNSMutableArray类的对象在该源码最后Block的执行部分超出其变量作用域而存在。转换后的源代码如下

struct __main_block_impl_0 {
  struct __block_impl impl;
  struct __main_block_desc_0* Desc;
  id array;
  __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, 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, id obj) {
  id array = __cself->array; // bound by copy

            ((void (*)(id, SEL, ObjectType _Nonnull))(void *)objc_msgSend)((id)array, sel_registerName("addObject:"), (id)obj);
            NSLog((NSString *)&__NSConstantStringImpl__var_folders_yx_f6xspfd14l98cwj9w_ngytvm0000gp_T_main_844042_mi_0, (long)((NSUInteger (*)(id, SEL))(void *)objc_msgSend)((id)array, sel_registerName("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};
int main(int argc, char * argv[]) {

//使用Block部分
    blk_t blk;
    {
        id array = ((NSMutableArray *(*)(id, SEL))(void *)objc_msgSend)((id)((NSMutableArray *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSMutableArray"), sel_registerName("alloc")), sel_registerName("init"));

        blk = (blk_t)((id (*)(id, SEL))(void *)objc_msgSend)((id)((void (*)(id))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, array, 570425344)), sel_registerName("copy"));
    }

    ((void (*)(__block_impl *, id))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, ((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("alloc")), sel_registerName("init")));
    ((void (*)(__block_impl *, id))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, ((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("alloc")), sel_registerName("init")));
    ((void (*)(__block_impl *, id))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk, ((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)((NSString *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("alloc")), sel_registerName("init")));

}

我们还是像之前一样分解开来看,先看结构体内的内容。

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

我们可以发现被截获自动变量array变成了Block结构体中附有__strong修饰符成员变量

其实在Objective -C中,C语言结构体不能含有附有__strong修饰符的变量。因为编译器不知道应该何时进行C语言结构体的初始化和废弃操作,不能良好的管理内存。但是Objective-C运行时库能够准确的把握Block从栈复制到堆以及堆上的Block被废弃的时机,因此Block结构体中即使含有附有__strong或者__weak修饰符的变量,也可以恰当的进行初始化和废弃。

为此需要使用在__main_block_desc_0结构体中增加的成员变量copydispose,以及作为指针赋值给该成员变量的__main_block_copy_0函数和__main_block_dispose_0函数。

由于在该源代码的Block结构体中,含有附有__strong修饰符的对象类型变量array,所以需要恰当管理赋值给变量array的对象。因此__main_block_copy_0函数使用_Block_object_assign函数将对象类型的对象赋值给Block结构体成员变量array中并持有该对象。

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, /*BLOCK_FIELD_IS_OBJECT*/);
}

_Block_object_assign函数调用相当于retain实例方法的函数,将对象赋值在对象类型的结构体成员变量中。

另外,__main_block_dispose_0函数使用_Block_object_dispose函数,释放赋值在Block结构体成员变量array中的对象。

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

_Block_object_dispose函数调用相当于release实例方法的函数,释放赋值在Block结构体成员变量array中的对象。

虽然此__main_block_copy_0函数和__main_block_dispose_0函数指针被赋值在__main_block_desc_0结构体成员变量copydispose中,但转换后的源代码中,这些函数包括使用指针全都没有被调用,因为这些只有在Block从栈复制到堆的时候以及堆上的Block被废弃时会被调用。

以下为上Block复制的几种情况:
1.调用Block的copy实例方法时。
2.Block作为参数返回值时。
3.将Block赋值给附有__strong修饰符id类型的类或Block类型成员变量时。
4.在方法名中含有usingBlockCocoa框架方法或GCDAPI中传递Block时。

在调用Block的copy实例方法时,如果Block配置在上,那么该Block会从栈复制到堆。Block作为函数返回值时,将Block赋值给附有__strong修饰符id类型的类或Block类型成员变量时,编译器自动的将对象的Block作为参数调用_Block_copy函数,这与调用Block的copy实例方法的效果相同。在方法名中含有usingBlockCocoa框架方法或GCD的API中传递Block时,该方法或函数内部对传递过来的Block调用Block的copy实例方法或者_Block_copy函数。

其实以上的情况都可归结为_Block_copy函数被调用的时候Block从栈复制到堆

相对的,在释放复制到堆上的Block后,谁都不持有Block而使其被废弃时调用dispose函数。相当于对象的dealloc方法。

有了这种构造,通过使用附有__strong修饰符的自动变量,Block中截获的对象就能够超出作用域而存在。

我们再来回顾一下上面的源码,如果没有copy方法会怎样?

blk_t blk;
    {
        id array = [[NSMutableArray alloc] init];
        
        blk = ^(id obj){
            [array addObject:obj];
            NSLog(@"Now the count of array = %ld", (long)[array count]);
        };
    }
    
    blk([[NSString alloc] init]);
    blk([[NSString alloc] init]);
    blk([[NSString alloc] init]);

执行该源码后,程序会强制结束,只有调用_Block_copy函数才能持有截获到的附有__strong修饰符的对象类型的自动变量值,所以像上面代码这样不调用_Block_copy函数的情况下,即使截获了对象,它也会随着变量的作用域结束而被废弃

相关文章

网友评论

      本文标题:Block笔记(五)

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