简述Block
block 是 C 语言的扩充功能,我们可以认为它是 带有自动变量的匿名函数,同时也是一个对象。
- 首先Block声明及定义语法,及其变形
1 标准定义
returnType(^blockName)(var_type) = ^returnType(var_type varName){ //操作 }
举个🌰 :
void(^defalutBlock)(NSString *) = ^void(NSString *name){ NSlog(@"%@",name)}
2 省略缩写
returnType(^blockName)(var_type) = ^(var_type varName){ //操作 }
举个🌰 :
void(^defalutBlock)(NSString *) = ^(NSString *name){ NSlog(@"%@",name)}
3 typedef简化Block的声明
typedef return_type (^BlockTypeName)(var_type);
- 接下来我们看几个面试题来分析下
1.
block
的原理是怎样的?本质是什么?
2.__block
的作用是什么?有什么使用注意点?
3.block
的属性修饰词为什么是copy
?使用block
有哪些使用注意?
4.block
在修改NSMutableArray
,需不需要添加__block
?
问题1: block原理是什么?本质是什么?
写个block
int main(int argc, char * argv[]) {
int(^block)(int)=^int(int a)
{
return a;
};
block(8);
return 0;
}
通过使用clang命令,将.m文件转换成.cpp文件clang -rewrite-objc main.m
int main(int argc, char * argv[]) {
int(*block)(int)=((int (*)(int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
((int (*)(__block_impl *, int))((__block_impl *)block)->FuncPtr)((__block_impl *)block, 8);
return 0;
}
C++定义block变量代码 其中不难看出 将__main_block_impl_0
函数的地址赋值给了block
int(*block)(int)=((int (*)(int))&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA));
我们接着看下__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;
}
};
注意
:__main_block_impl_0
结构体中 有同名的构造函数__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0)
其中的fp 可以理解为方法的地址
我们可以看到__main_block_desc_0中存储着两个参数,reserved和Block_size,并且reserved赋值为0而Block_size则存储着__main_block_impl_0的占用空间大小。最终将__main_block_desc_0结构体的地址传入__main_block_func_0中赋值给Desc
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)};
我们再来看下__block_impl
结构体
struct __block_impl {
void *isa;
int Flags;
int Reserved;
void *FuncPtr;
};
我们可以发现__block_impl结构体内部就有一个isa指针。因此可以证明block本质上就是一个oc对象。而在构造函数中将函数中传入的值分别存储在__main_block_impl_0结构体实例中,最终将结构体的地址赋值给block。
分析得出
- __block_impl结构体中isa指针存储着&_NSConcreteStackBlock地址,可以暂时理解为其类对象地址,block就是_NSConcreteStackBlock类型的
- block代码块中的代码被封装成__main_block_func_0函数,FuncPtr则存储着__main_block_func_0函数的地址。
- Desc指向__main_block_desc_0结构体对象,其中存储__main_block_impl_0结构体所占用的内存。
问题总结:
此时已经基本对block的底层结构有了基本的认识,上述代码可以通过一张图展示其中各个结构体之间的关系。
本文有参考其他作者:
block文章---xxcc