Block就是一个里面储存了指向函数体中包含定义block时的代码块的函数指针,以及block外部上下文变量等信息的结构体
1.为什么说Block是Objc对象 及内存分布
可以通过object_getClass 来获取对象的class
void(^blockName)() = ^() {
};
Class blockClass = object_getClass(blockName);
while (blockClass) {
NSLog(@"%@",blockClass);
blockClass = class_getSuperclass(blockClass);
}
image.png
总结: 有类的层级结构所以说是一个类
block的内存分布
有3种
globalBlock 全局
mallocBlock 堆区
stackBlock 栈区
1.没有外部变量的block都是globalBlock
2.有外部的变量有2种
2.1全局变量,静态变量,局部静态变量,
都是globalBlock
2.2普通外部变量,strong 和copy 修饰的是一样的放在堆区了,weak 放在栈区 (会被释放) 再次调用会造成野指针访问 如2.2图
2.2.png
3.block自定义的函数的参数的时候,block会在栈区,但是你调用了系统的方法,block就会自动copy变成mallocblock
4.block作为返回值的时候是在堆区的
image.png
2.不同block的结构
1.空的block
2.包含简单类型(int等)block
3.包含临时的objc对象的block
4.成员变量
5.包含__block的变量
6.global value
7.全局static value
8.局部static value
把.m文件变成C++文件
clang --help
-rewrite-objc Rewrite Objective-C source to C++
注意不是底层实现代码,是C++的实现代码,一定程度上能了解block在C++上怎么实现的 ,进而推断Block在运行时都干了什么
1.block
int age = 10;
void(^blockName)() = ^() {
NSLog(@"%d",i);
};
生成C++摘取主要的代码
struct __blcok_impl{
void *isa;//存放block 是那个block的(globle,stack malloc)
int Flags;
int Reserved;
void *FuncPtr;//执行函数的指针
}
struct __main_block_desc_0 {
size_t reserved;
size_t Block_size;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int a;
// C++ 的构造函数
//Fp 是要执行函数的指针
//desc 描述block的
//_age 外部捕获的参数
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc,int _age, int flags=0):age(_age) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
//执行的函数 其对应的就是*fp
static void __main_block_func_0(struct __main_block_impl_0 *__cself){
}
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变量
void (*block)(void) = &__main_block_impl_0(
__main_block_func_0,
&__main_block_desc_0_DATA);
// block 的调用
block->FuncPtr(block);
这样的话,我们就可以通过 void (*block)(void) 来接收这个结构体的地址,通过调用这个 FuncPtr 来调用函数。
结构.png
2捕获外部变量
1.空的block
2.包含简单类型(int等)block //值的捕获, 离开作用域就会销毁
3.包含临时的objc对象的block //对象的地址捕获 ,离开作用域就会销毁
4.成员变量 //成员变量的object 的捕获
5.包含__block的变量 //生成结构图
6.global value //直接使用 不用捕获
7.全局static value //直接使用不用捕获
8.局部static value //指针传递
3.__block的作用
修饰基本数据类型
__block int a = 8;
void (^block)(void) = ^{
a = 10;
NSLog(@"------");
};
block();
NSLog(@"-----%d",a);
首先经过 __block 修饰的 int a 会被转换为一个结构体__Block_byref_a_0 a,并将给这个结构体赋值
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding;
int __flags;
int __size;
int a;
};
__Block_byref_a_0 a = {
0,
&a,
0,
sizeof(__Block_byref_a_0),
8};
接下下来在void (^block)(void) 中给 a 赋值,通过 a->__forwarding->a,即:
static void __main_block_func_0(struct __main_block_impl_0 *__cself) {
__Block_byref_a_0 *a = __cself->a;
(a->__forwarding->a) = 10;
}
用__blcok修饰的基本数据类型
基本数据类型用__block修饰后,会生成2个一样结构的struct 一个放在栈区代替 基本数据,另一份拷贝堆区,让block能访问,通过struct中的__forwading 属性栈区的指向堆区的struct的地址,堆区的指向自己的地址,
实现 在堆区和栈区修改变量的值保持数据的一致性.
解决循环引用
__weak
__>unsafe_unretained
__b>lock
MRC 情况下 __block 不会对外部的变量产生强引用
总结:Block就是一个里面储存了指向函数体中包含定义block时的代码块的函数指针,以及block外部上下文变量等信息的结构体
ARC 下 修饰block 使用 strong 和 copy 没有区别,都会将栈上的拷贝到堆上
MRC 下 strong 强引用 copy 会将栈上的拷贝到堆上
网友评论