块的存储类型可分为栈类型(NSStackBlock)、堆类型(NSMallocBlock)、全局类型(NSGlobalBlock);
类型决定因素:
堆类型:引用了外部变量,并且将块对象赋给了强引用的变量或者属性
栈类型:引用了外部变量,但未被变量或者属性强引用,不计算引用计数,存放在栈空间
全局变量:未引用外部变量
存活时间:
栈类型:在声明块对象的方法返回后被autorelease回收
堆类型:引用计数变为0后销毁
全局类型:一直存在,编译时已经初始化
栈类型块调用崩溃的情况:
栈块所在的方法返回之后调用时,会发生崩溃。
ViewController *vc2 = [[ViewController alloc] init];
__weak typeof(ViewController) *weakvc = vc2;
void(^__weak block)(void);
block = ^(void){
NSLog(@"%@",weakvc);
};
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
block();
});
Thread 1: EXC_BAD_ACCESS (code=1, address=0x21)
如果是堆块的话,在dispatch_after中堆块被引用,引用计数+1,不会发生崩溃。
在栈块调用copy方法时返回对象是NSMallocBlock类型块:
NSStackBlock块在调用copy后返回NSMallocBlock类型的块,因为NSStackBlock生命周期直到方法返回,所以赋值给weak修饰的指针不会被立即释放,但如果NSMallocBlock类型的块赋值给weak修饰的指针会被立即释放。
NSMallocBlock类型块的延迟释放与循环引用
块内捕获的外部变量如果是__strong修饰(默认没有生命__weak则为__strong修饰),则在块内对其引用计数+1,否则不改变引用计数。
为了防止块内捕获的对象延迟释放,需要声明一个__weak修饰的变量,使该变量指向要引用的对象,在块内使用该weak指针捕获该变量,不会造成延迟释放。
同理,如果捕获的强引用变量指针所指向的对象对块有强引用,那么会造成循环引用。这时,如果在块内捕获的变量是_weak修饰的,可以避免循环引用。