Blocks
定义
Blocks,带有自动变量(局部变量)的匿名函数。
C 语言中的变量:
- 自动变量(局部变量)
- 函数的参数
- 静态变量(静态局部变量)
- 静态全局变量
- 全局变量
其中,静态变量(静态局部变量)、静态全局变量、全局变量 可以在函数的多次调用之间传递值。
语法
Objective-C 中 blocks 的语法
1 2 3 4 5 6 7 8 9 10 11
| ^void (int a) { NSLog("%d", a); }
^(int count) { return count + 1; }
^{ NSLog("abc"); }
typedef int (^blk_t)(int);
|
Block 实质
clang(LLVM 编译器),将 block 语法的代码,转变为 C++ 的源代码,观察 block 实质。
1
| clang -rewrite-objc main.m
|
没有截获自动变量的 block
1 2 3 4 5 6 7
| #include <stdio.h> int main() { void (^blk)(void) = ^{ printf("hello\n"); }; blk(); return 0; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47
|
struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; };
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)};
struct __main_block_impl_0 {
struct __block_impl impl; struct __main_block_desc_0* Desc;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } };
static void __main_block_func_0(struct __main_block_impl_0 *__cself) { printf("hello\n"); }
int main() {
&__main_block_desc_0_DATA);
void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA)); ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk); return 0; }
|
截获自动变量的 block
1 2 3 4 5 6 7 8 9 10
| #include <stdio.h> int main() { int dmy = 256; int val = 10; const char *fmt = "val = %d\n"; void (^blk)(void) = ^{ printf(fmt, val); }; blk(); return 0; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; };
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)};
struct __main_block_impl_0 {
struct __block_impl impl; struct __main_block_desc_0* Desc; const char *fmt; int 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; } };
static void __main_block_func_0(struct __main_block_impl_0 *__cself) { const char *fmt = __cself->fmt; int val = __cself->val; printf(fmt, val); }
int main() { int dmy = 256; int val = 10; const char *fmt = "val = %d\n";
void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, fmt, val)); ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk); return 0; }
|
静态变量 & 静态全局变量 & 全局变量
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <stdio.h> int global_val = 1; static int static_global_val = 2; int main() { static int static_val = 3; void (^blk)(void) = ^{ global_val *= 1; static_global_val *= 2; static_val *= 3; }; blk(); return 0; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| int global_val = 1; static int static_global_val = 2;
struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; };
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)};
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; } }; 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; }
int main() { static int static_val = 3; void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, &static_val)); ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk); return 0; }
|
补充:
为什么操作静态变量可以使用指针,而操作自动变量没有这么做?
因为 block 调用时,可能已经超过自动变量的作用域,自动变量的指针,超出其自身的作用域后就会被废弃,所以操作指针的方式只适用于静态变量。
__block
1 2 3 4 5 6 7 8 9
| #include <stdio.h> int main() { __block int val = 10; void (^blk)(void) = ^{ val = 1; }; blk(); return 0; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
| struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; };
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};
static void __main_block_copy_0(struct __main_block_impl_0*dst, struct __main_block_impl_0*src) {_Block_object_assign((void*)&dst->val, (void*)src->val, 8);}
static void __main_block_dispose_0(struct __main_block_impl_0*src) {_Block_object_dispose((void*)src->val, 8);}
struct __Block_byref_val_0 { void *__isa; __Block_byref_val_0 *__forwarding; int __flags; int __size; int val; };
struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __Block_byref_val_0 *val; __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; } };
static void __main_block_func_0(struct __main_block_impl_0 *__cself) { __Block_byref_val_0 *val = __cself->val; (val->__forwarding->val) = 1; }
int main() { __attribute__((__blocks__(byref))) __Block_byref_val_0 val = {(void*)0,(__Block_byref_val_0 *)&val, 0, sizeof(__Block_byref_val_0), 10}; void (*blk)(void) = ((void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA, (__Block_byref_val_0 *)&val, 570425344)); ((void (*)(__block_impl *))((__block_impl *)blk)->FuncPtr)((__block_impl *)blk); return 0; }
|
block 的存储域
block 是 objective-c 的对象。block 的类有三种,_NSConcreteStackBlock、_NSConcreteGlobalBlock、_NSConcreteMallocBlock。
类 |
设置对象的存储域 |
_NSConcreteStackBlock |
栈 |
_NSConcreteGlobalBlock |
程序的数据区域(.data 区) |
_NSConcreteMallocBlock |
堆 |
_NSConcreteGlobalBlock
- block 声明在全局变量的地方
- block 表达式中没有使用自动变量,虽然 clang 转换的源码通常是_NSConcreteStackBlock,但实现上不同
即,block 存储在程序的数据区域中。
_NSConcreteStackBlock
除了上述两种 global 情况外,其他 block 为 _NSConcreteStackBlock 对象,存储在栈上。
_NSConcreteMallocBlock
何时使用堆上的 _NSConcreteMallocBlock?
设置在栈上的 block 和 __block 对象,在离开了所属变量的作用域时,就会被废弃。
Blocks 提供了将 block 和 __block 对象,从栈上赋值到堆上的方法来解决这个问题。
复制到堆上的 block 将 _NSConcreteMallocBlock 类对象写入 block 的结构体实例的变量 isa
impl.isa = &_NSConcreteMallocBlock;
当 block 被复制到堆上时,block 使用的所有 **block 变量,也会被复制到堆上,此时 block 持有 **block 变量。
**block 对象的 **forwarding 指针,则会在复制到堆上后,指向堆上的 __block 结构体实例的地址。
什么时候栈上的 block 会被复制到堆上呢?
- 调用 block 的 copy 实例方法;
- block 作为函数的返回值
- 将 block 赋值给 __strong 修饰的 id 类型的类或 block 类型成员变量时;
- 在方法名中含有 usingBlock 的 Cocoa 框架方法,或者 GCD 的 API 中传递 block 时。
循环引用
如果在 block 中使用 strong 修饰符的对象的自动变量,那么当 block 从栈复制到堆上时,改对象被 block 持有。
此时可能产生循环引用。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
|
typedef void(^blk_t)(void);
@interface MyObject: NSObject { blk_t blk_; } @end
@implementation MyObject - (id)init { self = [super init]; blk_ = ^{ NSLog(@"self = %@", self); }; return self; } - (void)execBlock { blk_(); } - (void)dealloc { NSLog(@"dealloc"); } @end
int main() { id foo = [[MyObject alloc] init]; NSLog(@"%@", foo); return 0; }
|
上述代码中,dealloc 方法没有被调用。
- foo 强引用 blk_
- blk_ 强引用 foo。
解决方法:
使用 weak
1 2
| id __weak tmp = self; blk_ = ^{ NSLog(@"self = %@", tmp); };
|
- foo 强引用 blk_
- blk_ 弱引用 foo。
使用 __block
1 2 3 4 5 6 7 8
| __block id tmp = self; blk_ = ^{ NSLog(@"self = %@", self); tmp = nil; };
[foo execBlock];
|
- foo 强引用 blk_;
- blk_ 强引用 __block 修饰的 tmp;
- tmp 强引用 foo;
- 执行完 blk_(), tmp 对 foo 没有引用
简单起见,使用 weak 方法。