block 三种类型
| 全局 | 堆区 | 栈区 |
|---|
| 没有访问auto变量 | 栈区经过copy | 访问了auto变量 |
- 如果block访问外界变量,并进行block相应拷贝,即copy
- 如果此时的block是强引用,则block存储在堆区,即堆区bloc
- 如果此时的block通过__weak变成了弱引用,则block存储在栈区,即栈区block
- MRC环境下 访问了auto变量,并没有copy,即还是栈区block
int g = 666;
void (^ __weak block) (void) = ^ {
NSLog(@"%d",a);
}
block变量捕获底层
- 全局变量
- 直接访问,在全局区域。不需要__block修饰,即可改变值
- 局部变量
- auto变量,捕获到block内部,值捕获 (需要__block修饰才能改变外部变量的值)
- static,捕获到block内部,指针捕获。不需要__block修饰,即可改变值
关于__block底层实现
- __block用于解决block内部无法修改auto变量值的问题
- __block不能修改全局变量、静态变量
- __block原理
- 编译器会将外界变量会生成__Block_byref_a_0结构体(对象)
- 结构体用来保存原始变量的指针和值
- 将变量生成的结构体对象的指针地址 传递给block,然后在block内部就可以对外界变量进行操作了
__block int a = 666;
void(^block)(void) = ^{
a--;
printf("%d", a);
};
block();
struct __Block_byref_a_0 {
void *__isa;
__Block_byref_a_0 *__forwarding;
int __flags;
int __size;
int a;
};
struct __main_block_impl_0 {
struct __block_impl impl;
struct __main_block_desc_0* Desc;
__Block_byref_age_0 *age;
__main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, __Block_byref_age_0 *_age, int flags=0) : age(_age->__forwarding) {
impl.isa = &_NSConcreteStackBlock;
impl.Flags = flags;
impl.FuncPtr = fp;
Desc = desc;
}
};
block循环引用
- self持有block,block内部捕获了self,block持有self,互相引用,不能释放
- 解决循环引用
- self为block参数
- 使用NSProxy(参考YYWeakProxy)
- __weak
__weak typeof(self) weakSelf = self;
- __block修饰对象(需要注意的是在block内部需要置空对象,而且block必须调用)
__block ViewController *vc = self;
self.block = ^(void){
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
vc = nil;
});
};
self.block();
block本质
- 是一个oc对象,内部也有isa指针
- 封装了函数调用以及函数调用环境的OC对象