闭包是一个函数(或者指向函数的指针) + 函数执行的非局部变量。
闭包允许一个函数访问该函数运行上下文中的变量,甚至可以访问不用运行上文中的变量。
block是OC对闭包的实现。
1、block可以访问局部变量,全局变量,即使是static。
2、block中变量修改情况:
1)对于block外部的变量引用,默认是将其复制到数据结构中实现访问的,通过block进行闭包的变量是const的,不能直接修改这些变量。
2)__block修饰外部变量引用,block复制其引用地址来实现访问。
3、block结构体定义如下:
struct Block_layout{
void *isa;//指向表明该block类型的类
int flags;//按bit位标示一些的block的附加信息,比如判断block类型、判断block引用计数、判断block是否需要执行辅助函数等。
int reserved;//保留变量
void (* invoke)(void *,...);//函数指针,执行具体的block实现的函数调用地址。
struct Block_descriptor *descriptor;//block的附加信息,比如保留变量数,block的大小,进行copy活dispose得辅助函数指针。
/*Imported variables*/ //因为block有比暴行,所以可以访问block外部的局部变量。这些variables就是复制到结构体中的外部局部变量或变量的地址。
};
struct Block_descriptor{
unsigned long int reserved;
unsigned long int size;
void (*copy)(void *dst, void *src);
void (*dispose)(void *);
};
4、block的类型
block几种不同的类型,每个类型都有对应的类,isa指针就是指向这个类。
NSConcreteGlobalBlock: 全局的静态block,不会访问任何外部变量,不会涉及到任何拷贝。此类型的block要么是空block,要么不访问任何外部的block。
NSConcreteStackBlock: 保存在栈中的block,当函数返回时被销毁。此类型的block又闭包行为,也就是有访问外部变量的能力,且只有一次执行,执行一次就被清除栈了,无法多次使用。
NSConcreteMallocBlock: 保存在队中的block,引用计数为0时被销毁。此类型的block又闭包行为,且需要被多次执行,当需要多次执行时,会把该block从栈中复制到堆中,以供多次执行。
ARC与非ARC三种类型需要注意点事项:
1)只要不访问外部变量====》NSConcreatGlobalBlock
2) ARC下不存在NSConcreatStatckBlock,会自动拷贝到堆上
3)在非ARC下需要注意NSConcreatStatckBlock的生命周期
NSConcreatGlobalBolck 在ARC与非ARC下没有区别
NSConcreatStatckBlock 在ARC下会被转化为NSConcreatMallocBlock(由栈到堆) ,非ARC下不变
NSconcreatMallockBock 在ARC与非ARC下没有区别
5、block执行原理
int main()
{
__block int i = 1024;
int j = 1;
void (^ blc)(void);
blk = ^{printf("i:%d, j:%d, &i=%p, &j:%p",i,j,&i,&j);};
}
=======>>>
int main()
{
__attribute__((__blocks__(byref))) __Block_byref_i_0 i = {(void*)0,(__Block_byref_i_0,_main_block_disponse_0)};
int j = 1;
void (*blk)(void);
blk = (void (*)())&__main_block_impl_0((void *)__main_block_func_0,&__main_block_desc_0_DATA,j,(__Block_byref_i_0 *)&i,)
blk();
等价于(void (*) (__block_impl *)) ((__block_impl *)blk) -->FuncPtr)((__block_impl *)blk);
}
block是一个指向__main_block_impl_0结构体指针,初始化后被强转为函数指针,其中包含的__block_impl是一个公共实现。