《Objective-C高级编程》Blocks 阅读笔记系列
《Objective-C高级编程》Blocks 阅读笔记 item1(Blocks概要和模式)
《Objective-C高级编程》Blocks 阅读笔记 item2(Block的实质)
《Objective-C高级编程》Blocks 阅读笔记 item3(截获自动变量值)
《Objective-C高级编程》Blocks 阅读笔记 item4(__block说明符)
《Objective-C高级编程》Blocks 阅读笔记 item5(Block存储域)
《Objective-C高级编程》Blocks 阅读笔记 item6(__block变量存储域)
《Objective-C高级编程》Blocks 阅读笔记 item7(截获对象)
《Objective-C高级编程》Blocks 阅读笔记 item8(__block变量和对象)
《Objective-C高级编程》Blocks 阅读笔记 item9(Block循环引用)
《Objective-C高级编程》Blocks 阅读笔记 item10(copy/release实例方法)
2.3 Blocks的实现
2.3.3 __block说明符
从“截获自动变量值”一节中,可以发现:*** Block中所使用的被截获的自动变量就如“带有自动变量值的匿名函数”所说,仅截获自动变量的值。Block中使用自动变量后,在Block结构体实例中重写该自动变量也不会改变原先截获的自动变量。 ***
因此,在Block中无法保存值。为了解决这个问题,有两种方案:
- 使用C语言的静态全局变量/全局变量可以允许Block改写值,并直接使用;而静态变量则由于变量作用域的原因,无法访问。
- 使用__block说明符。
第一种方案
int global_val = 1; //全局变量
static int static_global_val = 2; // 静态全局变量
int main(int argc, const char * argv[]) {
static int static_val = 3; // 静态变量
void (^blk)(void) = ^{
global_val *= 1;
static_global_val *= 2;
static_val *= 3;
};
return 0;
}
经clang转换后:
int global_val = 1; //全局变量
static int static_global_val = 2; // 静态全局变量
// 结构体 __block_impl
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
// 结构体 __main_block_impl_0
struct __main_block_impl_0 {
// 成员变量
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int *static_val; // 原先的源代码中使用的静态变量被追加为成员变量
// 构造函数
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int *_static_val, int flags=0) : static_val(_static_val) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
// 静态函数 __main_block_func_0 (Block语法表达式发生的转换)
static void __main_block_func_0(struct __main_block_impl_0 *__cself)
{
int *static_val = __cself->static_val;
global_val *= 1;
static_global_val *= 2;
(*static_val) *= 3;
/*
理解:
1. 使用静态变量static_val的指针对静态变量进行访问
2. 将静态变量static_val的指针传递给__main_block_impl_0结构体的构造函数并保存,这是超出作用域,使用静态变量的最简单方法
*/
}
// 静态结构体 __main_block_desc_0
static struct __main_block_desc_0{
unsigned long reserved;
unsigned long Block_size;
} __mian_block_desc_0_DATA = {
0,
sizeof(struct __main_block_impl_0)
};
// 主函数,从这里开始阅读源代码
int main()
{
static int static_val = 3; // 静态变量
// 调用__main_block_impl_0结构体实例的构造函数,并将静态变量static_val的指针作为参数传递给构造函数
blk = &__main_block_impl_0(__main_block_func_0, &__mian_block_desc_0_DATA, &static_val);
return 0;
}
总结
*** 超出作用域使用指针访问静态变量的这种方法并不适用于自动变量的访问。***
*** 实际上,在由Block语法生成的值Block上,可以存有超过其变量作用域的被截获对象的自动变量。变量作用域结束的同时,原来的自动变量被废弃,因此Block中超过变量作用域而存在的变量,将不能通过指针访问原来的自动变量。 ***
第二种方案
*** 使用__block说明符(存储类说明符)。***
C语言中的存储类说明符
- typedef
- extern
- static —— *** 表示作为静态变量存储于数据区中 ***
- auto —— *** 表示作为自动变量存储于栈中 ***
- register
__block说明符类似于static、auto和register说明符,它们用于*** 指定将变量值设置到哪个存储域。***
- __block —— *** 用来指定Block中想变更值的自动变量 ***
__block int val = 10;
void (^blk)(void) = ^{val = 1;};
经clang转换,如下:
// 结构体 __block_impl
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
// 结构体 __Block_byref_val_0
struct __Block_byref_val_0 {
// 成员变量
void *__isa;
__Block_byref_val_0 *__forwarding;
int __flags;
int __size;
int val; // 相当于原自动变量的成员变量
};
// 结构体 __main_block_impl_0
struct __main_block_impl_0 {
// 成员变量
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_val_0 *val; //“持有相当于原自动变量的成员变量”的“__main_block_impl_0结构体实例”被追加到成员变量中
// 构造函数
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_val_0 *_val, int flags =0) : val(_val->__forwarding){
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
// 静态函数 __main_block_func_0 (Block语法表达式发生的转换)
static void __main_block_func_0(struct __main_block_impl_0 *__cself)
{
__Block_byref_val_0 *val = __cself->val;
(val->__forwarding->val) = 1;
/*
理解:
1. Block的__main_block_impl_0结构体实例持有指向“__block变量的__Block_byref_val_0结构体实例”的指针(即__Block_byref_val_0 *val)
2. __Block_byref_val_0结构体实例的成员变量__forwarding持有指向”该实例自身“的指针
3. 因此,通过__Block_byref_val_0结构体实例的成员变量__forwarding可以访问该结构体实例的成员变量val
*/
}
// 静态函数 __main_block_copy_0
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src){
_Block_object_assign(&dst->val, src->val, BLOCK_FIELD_IS_BYREF);
}
// 静态函数 __main_block_dispose_0
static void __main_block_dispose_0(struct __main_block_impl_0*src){
_Block_object_dispose(src->val, BLOCK_FIELD_IS_BYREF);
}
// 静态结构体 __main_block_desc_0
static struct __main_block_desc_0{
unsigned long reserved;
unsigned long Block_size;
void (*copy)(struct __main_block_impl_0*, struct __main_block_impl_0*);
void (*dispose)(struct __main_block_impl_0*);
} __mian_block_desc_0_DATA = {
0,
sizeof(struct __main_block_impl_0),
__main_block_copy_0,
__main_block_dispose_0
};
// 主函数,从这里开始阅读源代码
int main()
{
__Block_byref_val_0 val = {
0,
&val,
0,
sizeof(__Block_byref_val_0),
10
};
/*
理解:
1. __block变量会变成__Block_byref_val_0结构体类型的自动变量(即栈上生成的__Block_byref_val_0结构体实例)。
2. 该自动变量被初始化为10,这个值也出现在结构体实例的初始化中,意味着该结构体持有相当于原自动变量的成员变量。
*/
blk = &__main_block_impl_0(__main_block_func_0, &__mian_block_desc_0_DATA, &val, 0x22000000);
return 0;
}
访问__block变量
在多个Block中访问同一个__block变量
如果仔细观察,可以发现: __block变量的__Block_byref_val_0结构体实例并不在Block中的__main_block_impl_0结构体实例中,这样做的目的是为了在多个Block中使用同一个__block变量。