内存-②Block
[toc]
iOS-Block本质
block本质上也是一个OC对象,它内部也有个isa指针,最终继承NSObject。
如
1 | NSGlobalBlock <--__NSGlobalBlock <--NSBlock <-- NSObject |
Q:block有哪几种类型 及 各类型的block在内存中如何分配的?
block的类型,取决于isa指针,可以通过调用class方法或者isa指针查看具体类型,最终都是继承自NSBlock类型。
堆:动态分配内存,需要程序员自己申请,程序员自己管理
栈:自动分配内存,自动销毁,先入后出,栈上的内容存在自动销毁的情况
NSGlobalBlock 在数据区
1
2
3void (^block1)(void) = ^{
NSLog(@"block1");
};NSMallocBlock 在堆区
1
2
3
4int age1 = 1;
void (^block2)(void) = ^{
NSLog(@"block2:%d", age1);
};NSStackBlock 在栈区
1
2
3
4int age2 = 2;
NSLog(@"%@", [^{
NSLog(@"block3:%d", age2);
} class]);
附:打印block类型NSLog(@"%@", [block1 class]);
问:以下各情况,Person的对象什么时候才销毁?或Person对象在block上是如何操作的?
有Person类如下:
1 | #import "Person.h" |
了解解题的基础知识:
1、当对象类型的auto变量被block内部访问时,是强引用还是弱引用?(延伸:引用过后,block内部无法修改auto变量值,除非使用__block)
- 如果block在
栈
空间,不管外部变量是强引用还是弱引用,block都会弱引用访问对象 - 如果block在
堆
空间,如果外部强引用,block内部也是强引用;如果外部弱引用,block内部也是弱引用
2、GCD API的方法参数block,在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上。
附:在ARC环境下,编译器会根据情况自动将栈上的block复制到堆上的几种情况?
1.block作为函数返回值时
2.将block赋值给__strong指针时
3.block作为Cocoa API中方法名含有usingBlock的方法参数时
4.block作为GCD API的方法参数时
1
2
3dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
});
1、问: 下列代码中的Person的对象什么时候才销毁?
1 | -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ |
答:方法代码执行完就person就销毁了
2、问:以下gcd的block中引用 Person的对象什么时候才销毁?
1 | -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ |
答:
1、gcd的block默认会做copy操作,即dispatch_after的block是堆block。
2、我们知道如果block在堆空间,如果外部强引用,block内部也是强引用;如果外部弱引用,block内部也是弱引用
。
所以这里外部的Person没有声明为__weak,所以堆block会对Person强引用,故而也就只有block销毁时候Person才会被释放。这里是2秒后销毁。(如果没有gcd)
3、续问:如果上述Person被添加上__weak修饰,那Person什么时候释放?会造成什么问题?
续答:此时上述的代码会变为
1 | -(void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event{ |
同样,还是我们知道如果block在堆空间,如果外部强引用,block内部也是强引用;如果外部弱引用,block内部也是弱引用
。所以当Person被添加上__weak修饰后,堆block会对Person弱引用。而在touchesBegan函数结束后,由于Person就会被释放,所以2秒后gcd无法捕捉到Person的后果。即上述的输出结果是
1 | person 是 nil,打印的%p地址,是0x0。 |
4、上述代码使用__weak后,变量被释放了,那怎么防止block持有的对象提前释放
Q:block的属性修饰词为什么是copy?
block一旦没有进行copy操作,就不会在堆上
block在堆上,程序员就可以对block做内存管理等操作,可以控制block的生命周期
Q:当block被copy到堆时,对__block修饰的变量做了什么?
- 会调用block内部的copy函数
- copy函数内部会调用_Block_object_assign函数
- _Block_object_assign函数会对__block变量形成强引用(retain)
- 对于__block 修饰的变量 assign函数对其强引用;对于外部对象 assign函数根据外部如何引用而引用
Q:当block从堆中移除时,对__block修饰的变量做了什么?
- 会调用block内部的dispose函数
- dispose函数内部会调用_Block_object_dispose函数
- _Block_object_dispose函数会自动释放引用的__block变量(release)