Block的深入研究之Block的内存管理
2016-03-31 02:39
260 查看
一 非ARC中的Block内存管理
1 先介绍内存的五大区:堆区;栈区;方法区;静态区(全局区);常量区
2 非ARC环境:
—-> 2.1 block在没有访问外部局部变量,存放在内存的全局区—-> 具体代码演示:
- (void)viewDidLoad { [super viewDidLoad]; void(^block)() = ^{ }; NSLog(@"%@",block); block(); }
—-> 打印不出来的结果显示:<[b]NSGlobalBlock: 0x1071ba080>说明结论是对的[/b]
—->2.2 block访问外部局部变量,block存放栈区里面
—-> 具体代码演示:
- (void)viewDidLoad { [super viewDidLoad]; int a = 10; void(^block)() = ^{ NSLog(@"%d",a); }; NSLog(@"%@",block); block(); }
—-> 打印出来的结果显示:<[b]NSStackBlock: 0x7fff5a12f958>说明结论是对的[/b]
—-> 2.3 只要block访问变量,是整个app都存在的变量,那么肯定是在全局区
—-> 具体代码演示:
static int a = 10; - (void)viewDidLoad { [super viewDidLoad]; void(^block)() = ^{ NSLog(@"%d",a); }; NSLog(@"%@",block); block(); }
—-> 打印出来的结果显示:<[b]NSGlobalBlock: 0x10beff080>说明结论是对的[/b]
二 非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(); }
—-> 打印显示的结果:<[b]NSStackBlock: 0x7fff504f0960>说明结论是对的[/b]
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]; //block方法 int a = 4; void(^block)() = ^{ NSLog(@"%d",a); }; NSLog(@"%@",block); block(); self.block = block; }
—-> 打印显示的结果:<[b]NSMallocBlock: 0x7fd193da9800>说明结论是对的[/b]
五 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]; //model出来 [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 = ^{ //很多时候我们有可能在block块中做延迟操作,但是有可能我们会造成循环引用. dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t) (2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ //这里打印的结果为null,不能拿到控制器去做其它的事情. 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; //很多时候我们有可能在block块中做延迟操作,但是有可能我们会造成循环引用. dispatch_after(dispatch_time(DISPATCH_TIME_NOW,(int64_t) (2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{ //这里打印的结果为null,不能拿到控制器去做其它的事情. NSLog(@"1----%@",strongSelf); }); NSLog(@"2----%@",strongSelf); }; _block();
—-> 1 打印结果:
—-> 1.1 //2—-
—-> 1.2 //1—-
—-> 1.3//控制器被销毁了
—-> 2 结论:这样做就能让对象在执行完延迟糙所的时候让对象销毁,达到了目的.
具体的图形解答疑惑:
六 总结
这里面介绍了大部分block运用会出现的情况,更多的还是block在内存当中的管理情况,或许介绍的还不是很完整,后期会跟进补充的,大家有什么意见,好的坏的可以给我留言,谢谢!!!!
相关文章推荐
- Java多线程基础:进程和线程之由来
- Java并发编程:Thread类的使用
- LeetCode Count Primes
- 视频播放器(四)——总结篇
- Thread详解12:InheritableThreadLocal的使用
- QT20 database values in QLineEdit or textbox if select combobox
- Java并发编程:如何创建线程?
- Java_chapter20_递归
- 利用 Behavior Driven Development 技术加强软件自动化测试
- QT19 How to link QComboBox with sqlite Database values
- java android中对list的时间进行排序
- 项目坑
- arcgis for android 无法加载本地jpg影像解决办法
- QT18 how to link QListView with sqlite Database values
- 面向对象编程的原则
- 设计模式之 抽象工厂模式的扩展(C++实现)
- Java并发编程:volatile关键字解析
- Qt17 load sqlite table data to qtableview
- 转:典型错误:locale.Error:unsupported locale setting
- android studio 使用技巧