1 一个Block 真正的底层都有些什么?
Block会被解析成一个结构体(这里成为Block结构体),这个结构体里有:
(1)isa指针(说明Block的本质是一个对象):指向Stack、堆
(2)有函数指针(这个函数指针指向一个函数体,该函数体的内容就是Block函数体的内容)。该函数的【参数表】里面有一个参数为【这个Block结构体类型】的参数,在调用这个Block 函数的时候,会把该函数所属Block结构体作为参数值传入。函数可通过这个Block参数读取里面的捕获变量。这就是实现捕获外部变量的手段。
(3)如果Block有使用到外部变量,该结构体还会有捕获的变量(以结构体成员变量的形式存在,使用到的外部变量有多少个,就有多少个成员变量)捕获变量在结构体里面的声明没有改变。(就是跟Block捕获到的时候的声明一模一样)
2 Block捕获变量
【Block捕获自动变量(正常的Block,没有__block介入)】:Block使用到的变量会被存储(值拷贝)到Block结构体实例(的成员变量/属性)中。这个捕获发生在Block创建的时候(即:将捕获到的值传递给【Block结构体】的构造函数进行保存)。
2.1 在Block内改变捕获变量的值
在Block内可以改变【Block外部变量的值】的方式:
(1)C语言的:静态(局部)变量、静态全局变量、全局变量(P103-P104)
(2)使用__block说明符
【Block不捕获全局变量】:Block 只会捕获局部变量,对于全局变量、全局静态变量,是不会捕获的。这些全局变量的作用域是全局的,在Block中也能生效。
【Block捕获局部静态变量】:捕获的是该变量的指针(该变量地址)
【Block捕获__block修饰的变量】:__block 的变量会变成一个__block结构体(含有isa指针)P105,该结构体内部保存了捕获的变量值(__block结构体成员变量)。Block捕获的是该结构体指针。
2.2 OC中的存储域类说明符(__block)
所谓存储域类说明符,就是指定将变量值存放到哪个存储区域中(堆、栈、寄存器)
在C语言中,存储域类说明符有:
extern:声明所修饰的内容(变量/函数)能否为外部文件访问——变量/函数作用域
static:(修饰变量的时候)表示作为静态变量存储在静态变量区;修饰函数的时候,表示该函数只能在当前文件内访问,外文文件不可访问——函数作用域
auto:表示作为自动变量存储在栈中
register:表示尽可能将变量存储到CPU寄存器中
2.3 对捕获变量使用__block带来的改变
(1)【使用__block前】:捕获的变量是通过【值传递】赋值给Block结构体内的属性A(通过构造函数)进行保存,在Block函数体内又通过【值传递】(读取Block结构体的A属性)赋值给捕获的变量,这都是只读的,因此,我们在Block函数内不能修改捕获变量的值,否则在编译的时候就会报错。
(2)【使用__block后】:被捕获的变量不再是简单的值传递。而是在此之前,生成一个专属的【捕获变量结构体】,在该结构体中,持有与原捕获变量一摸一样的结构体成员。前面所说的在底层上对捕获变量的所有操作,都是在操作这个新创建的【捕获变量结构体】内保存的结构体成员。如此一来,通过修改【捕获变量结构体】中的结构体成员,就可以达到在Block内修改捕获变量的目的。