(三)Block之截获变量和对象
2017-10-21 本文已影响22人
madaoCN
相关文章
- (一)Block的实质初探
- (二)Block之存储域 NSConcreteStackBlock,NSConcreteGlobalBlock,NSConcreteMallocBlock
- (三)Block之截获变量和对象
- (四)Block之 __block修饰符及其存储域
捕获自动变量
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时候