一 非ARC中的Block内存管理
1 先介绍内存的五大区:堆区;栈区;方法区;静态区(全局区);常量区
2 非ARC环境:
—-> 2.1 block在没有访问外部局部变量,存放在内存的全局区
—-> 具体代码演示:
- (void)viewDidLoad {
[super viewDidLoad];
void(^block)() = ^{
};
NSLog(@"%@",block);
block();
}
—-> 打印不出来的结果显示:<NSGlobalBlock: 0x1071ba080>说明结论是对的
—->2.2 block访问外部局部变量,block存放栈区里面
—-> 具体代码演示:
- (void)viewDidLoad {
[super viewDidLoad];
int a = 10;
void(^block)() = ^{
NSLog(@"%d",a);
};
NSLog(@"%@",block);
block();
}
—-> 打印出来的结果显示:<NSStackBlock: 0x7fff5a12f958>说明结论是对的
—-> 2.3 只要block访问变量,是整个app都存在的变量,那么肯定是在全局区
—-> 具体代码演示:
static int a = 10;
- (void)viewDidLoad {
[super viewDidLoad];
void(^block)() = ^{
NSLog(@"%d",a);
};
NSLog(@"%@",block);
block();
}
—-> 打印出来的结果显示:<NSGlobalBlock: 0x10beff080>说明结论是对的
二 非ARC不能使用retain使用copy的原因
1 首先将定义block属性的copy修改为retain(会有警告,先不管)
@property (nonatomic, retain) void(^block)();
2 在没有调用block的时候,执行的block是在堆区
- (void)viewDidLoad {
[super viewDidLoad];
void(^block)() = ^{
NSLog(@"%@",block);
};
NSLog(@"%@",block);
self.block = block;
block();
}
—-> 打印显示的结果:<NSStackBlock: 0x7fff504f0960>说明结论是对的
3 当点击屏幕调用block的时候就会报错
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
self.block();
}
—-> 错误原因:坏的内存访问
—-> 得出的结论:在非ARC当中,不能使用retain引用block,不会把block放在堆区中,在非ARC当中,只能使用copy,才能把block放入堆区中.
三 在非ARC开发当中注意点
1 访问属性,不要直接使用_,而是通过set,get方法去访问
2 非ARC中没有weak -> assign,strong -> retain
四 ARC环境的Block内存管理
—-> 结论:block访问外部局部变量,block存放在堆区里面.可以通过使用strong去引用.
—-> 实例验证结论
- (void)viewDidLoad {
[super viewDidLoad];
int a = 4;
void(^block)() = ^{
NSLog(@"%d",a);
};
NSLog(@"%@",block);
block();
self.block = block;
}
—-> 打印显示的结果:<NSMallocBlock: 0x7fd193da9800>说明结论是对的
五 Block的循环引用
1 Block简单的循环引用
注意点: block只要访问外部强指针对象变量,就会对这个变量进行强引用.
1.1 实例代码一:(点击控制器的view,model出一个控制器)
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
XFJModelViewController *model = [[XFJModelViewController alloc] init];
model.view.backgroundColor = [UIColor redColor];
[self presentViewController:model animated:YES completion:nil];
}
—> 问题一:控制器已过大括号会被销毁么?
—> 解答:不会.
—> 原因:presentViewController:该方法底层还会做一个事情,就是被presentedViewController强指针引用,再说了,如果出了大括号就会被销毁,那么我们在model出来的控制器中dissmis又怎么会有效果呢.所以model出来的控制器是不会被销毁的,知道dissmis的时候才销毁.(但是排除其它情况会让控制器销毁,我们离就不一一列举了)
1.2 实例代码块二:
—->1.2.1 定义一个在model出来的控制器中定义一个block属性,然后在viewDidLoad里面对block赋值
—->1.2.2 运行的结果是当点击控制器dissmiss的时候,控制器被销毁
[self dismissViewControllerAnimated:YES completion:nil];
—-> 1.2.3 控制器销毁的时候调用了dealloc方法.
1.3 实例代码块三:
- (void)viewDidLoad {
[super viewDidLoad];
int a = 6;
_block = ^{
NSLog(@"%@",self);
};
}
—> 1 在block块里面访问了self,强指针.
—> 2 得出的结论:model出来的控制器不会被销毁,因为没有调用dealloc方法.
画图解答疑问:
解决办法:将self变成弱指针.
具体实施方案:
__weak typeof(self) weakself = self;
_block = ^{
NSLog(@"%@",weakself);
};
2 复杂的block循环引用
注意:在开发当中我们有些时候,需要在block块中做一些延迟操作,但是我们很多时候无法保住对象的生命周期,往往我们还没有执行到延迟操作的时候,对象就被销毁了.
具体代码:
__weak typeof(self) weakself = self;
_block = ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t) (2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"1----%@",weakself);
});
NSLog(@"2----%@",weakself);
};
_block();
—-> 1 打印的结果:
—-> 1.1 //2—-
—-> 1.2 //控制器被销毁了
—-> 1.3 //1—-(null)
—-> 2 这样就造成了,我们没发拿到对象去做延迟操作.
—-> 3 解决的思路:在言辞操作之前定义一个__strong,让对象运行完后保证不死.
代码块:
__weak typeof(self) weakself = self;
_block = ^{
__strong typeof(weakself) strongSelf = weakself;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t) (2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"1----%@",strongSelf);
});
NSLog(@"2----%@",strongSelf);
};
_block();
—-> 1 打印结果:
—-> 1.1 //2—-
—-> 1.2 //1—-
—-> 1.3//控制器被销毁了
—-> 2 结论:这样做就能让对象在执行完延迟糙所的时候让对象销毁,达到了目的.
具体的图形解答疑惑:
六 总结
这里面介绍了大部分block运用会出现的情况,更多的还是block在内存当中的管理情况,或许介绍的还不是很完整,后期会跟进补充的,大家有什么意见,好的坏的可以给我留言,谢谢!!!!