block
底层就是一个struct __main_block_impl_0
类型的结构体
,这个结构体中包含一个isa
指针,本质上是一个OC
对象block
是封装了函数调用
以及函数调用环境
的OC
对象block底层结构 block
底层结构就是__main_block_impl_0
结构体,内部包含了impl结构体
和Desc结构体
以及外部须要访问的变量
,block
将须要执行的代码放到一个函数里,impl
内部的FuncPtr
指向这个函数的地址,经过地址调用这个函数,就能够执行block
里面的代码了。Desc
用来描述block
,内部的reserved
做保留,Block_size
描述block
占用内存 git
block的变量捕获 局部变量block
访问方式是值传递
,auto自动变量
可能会销毁,内存可能会消失,不采用指针访问; 局部静态变量block
访问方式是指针传递
,static变量
一直保存在内存中,指针访问便可; 全局变量、静态全局变量block
不须要对变量捕获,直接取值 github
// block的变量捕获代码解析以下
auto int age = 10;
static int height = 10;
void (^block)(void) = ^{
NSLog(@"age is %d,height is %d",age,height);
};
age = 20;
height = 20;
block();
-------------------------------------------------
output: age is 10,height is 20
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
int age; // 值传递
int *height; // 指针传递
}
复制代码
block类型 | 环境 | 存储域 | copy操做后 |
---|---|---|---|
__NSGlobalBlock__ |
没有访问auto变量 |
数据区 | 什么也不作,类型不改变 |
__NSStackBlock__ |
访问了auto变量 |
栈区 | 从栈复制到堆,类型改变为__NSMallocBlock__ |
__NSMallocBlock__ |
__NSStackBlock__ 调用了copy |
堆区 | 引用计数+1 ,类型不改变 |
ARC
环境下,编译器会根据如下几种状况自动将栈上的block
复制到堆上
: 一、block
做为函数返回值时,好比使用=
二、将block
赋值给__strong
指针时 三、block
做为Cocoa API
中方法名含有usingBlock
的方法参数时 四、block
做为GCD API
的方法参数时block
内部访问了对象类型的auto变量
时: 若是block在栈空间
,不管是ARC仍是MRC
环境,无论外部变量
是强引用仍是弱引用
,block
都会弱引用
访问对象 若是block在堆空间
,若是外部强引用
,block
内部也是强引用
;若是外部弱引用
,block
内部也是弱引用
block
是在栈上
,将不会对auto变量
产生强引用
b) 栈上的block
随时会被销毁,也不必去强引用其余对象block
内部的copy
函数 b) copy
函数内部会调用_Block_object_assign
函数 c) _Block_object_assign
函数会根据auto变量
的修饰符__strong
、__weak
、__unsafe_unretained
作出相应的操做,造成强引用
或者弱引用
二、若是block从堆上移除 a) 会调用block
内部的dispose
函数 b) dispose
函数内部会调用_Block_object_dispose
函数 c) _Block_object_dispose
函数会自动释放引用的auto变量
(release
,引用计数-1
,若为0
,则销毁)__block
修饰符做用: __block
能够用于解决block
内部没法修改auto变量值
的问题 __block
不能修饰全局变量、静态变量static
__block
修饰符原理: 编译器会将__block
变量包装成一个结构体__Block_byref_age_0
,结构体内部*__forwarding
是指向自身的指针,内部还存储着外部auto变量
的值 __block
的forwarding
指针以下图:
栈上
,__block
结构体中的__forwarding
指针指向本身
,一旦复制到堆上
,栈上的__block
结构体中的__forwarding
指针会指向堆上的__block
结构体,堆上__block
结构体中的__forwarding
仍是指向本身
。假设age
是栈上
的变量,age->__forwarding
会拿到堆上的__block
结构体,age->__forwarding->age
会把20
赋值到堆上
,不管是栈上仍是堆上的__block
结构体,都能保证20
赋值到堆的结构体
里bash
int main(int argc, const char * argv[]) {
@autoreleasepool {
NSMutableArray *array = [NSMutableArray array];
void (^block)(void) = ^{
[array addObject: @"5"];
[array addObject: @"5"];
NSLog(@"%@",array);
};
block();
}
return 0;
}
复制代码
分析:能够正确执行,由于在block
块中仅仅是使用了array的内存地址
,往内存地址
中添加内容
,并无修改arry的内存地址
,所以array
不须要使用__block修饰
也能够正确编译。当仅仅是使用局部变量的内存地址
,而不是修改
的时候,尽可能不要添加__block
,经过上述分析咱们知道一旦添加了__block
修饰符,系统会自动建立相应的结构体,占用没必要要的内存空间函数
附:个人博客地址ui