Block概念
Objective-C 中的Block 本质是一个对象。 也可以把block理解为闭包。
Block的创建和使用
void (^block变量名)(NSString* 参数1...);
block变量名 = ^(NSString* 参数1...){
函数体
};
block变量名();
在GCD中给我们提供了一个无返回值,无参数的block定义。
@property (nonatomic, copy) dispatch_block_t block;
Block的实现
struct Block_descriptor {
unsigned long int reserved;
unsigned long int size;
void (*copy)(void *dst, void *src);
void (*dispose)(void *);
};
struct Block_layout {
void *isa;
int flags;
int reserved;
void (*invoke)(void *, ...);
struct Block_descriptor *descriptor;
/* Imported variables. */
};
分析
- isa指针,代表着Block本身也是被当做一个对象进行处理的。
- flags 用于按bit位表示一些block附加信息。
- reserved 保留变量
- invoke 函数指针,指向block实现的函数调用地址。
- descriptor 表示该block的附加描述信息。
- size,大学
- copy 拷贝函数
- dispose 释放函数
- variables 捕获的变量
Block分类
NSConcreteGlobalBlock
不捕获变量。
NSConcreteStackBlock
分配到占上的实例。
思考
- 既然Block是一个对象,为什么会被分配到栈上?为什么要把他分配到栈上?这样设计有什么好处?
NSConcreteMallocBlock
堆Block,通常不会在源码中出现。默认当一个block在被copy时,才会将block复制到堆上。
Block变量捕获
- 对于Block外变量的引用,block默认将其复制到数据结构中实现访问。
- 对于__block修饰的外部变量引用,block是将其复制到其引用地址实现访问的。
__block 原理
struct __Block_byref_i_0 {
void *__isa;
__Block_byref_i_0 *__forwarding;
int __flags;
int __size;
int i;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_i_0 *i; // by ref
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_i_0 *_i, int flags=0) : i(_i->__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_i_0 *i = __cself->i; // bound by ref
printf("%d\n", (i->__forwarding->i));
(i->__forwarding->i) = 1023;
}
...
- 使用__block后,捕获的变量以一个
__Block_byref_i_0 的形式存在,同时在__main_block_func_0 中使用的事__Block_byref_i_0 的结构体指针,保证改变的是外部的变量的值。 __Block_byref_i_0 结构体中保存的是捕获的外部变量的地址,forwarding的作用是在拷贝到堆上后,保证指针指向的是堆上的地址。
Objective-C 中的block
在ARC 模式下,将只有全局block 和堆block
Block的使用场景
- GCD 使用block做任务回调。
- 使用Block进行通信。
Swift的闭包
参考文章
[1] https://blog.devtang.com/2013/07/28/a-look-inside-blocks/
|