项目问题总结:Block内存泄露 以及NSTimer使用问题

BLock的内存泄露

  在我们代码中关于block的使用可以说随处可见,第一次接触block的时候是关于UIView的块动画,那时觉得block的使用好神奇,再后来分析总结为block其实就是一个c语言函数,只是我们可以在任意处调用此函数。有了这样的理解我开始经常使用block。在做项目以后发现使用block竟然会引起内存泄露,于是开始自己调试研究block的内存管理问题。

普通的block使用(包括块动画)

这里有一个简单的block使用,在里面我们可以添加任何自己想进行的操作,大部分的使用也是如此

1 void (^Block)(int) = ^(int num){
2    //此处还可添加其他代码 ....
3    NSLog(@"int number is %d",num);
4 };

包括UIView的块动画也是如此使用,在这里我们定义了一个图像视图的位置及透明度的变化

1 [UIView animateWithDuration:2.0 animations:^(void){
2     smallImage.frame = CGRectMake(150, 80, 30, 30);
3 } completion:^(BOOL finished) {
4      smallImage.alpha = 0;

这些block操作中,我一直都认为block中的对象会在block使用后就被释放(但UIView的操作好像是这么做的)

block内存管理初现

  直到我在项目中遇见这样一个情况:我设置有2个控制器first及second,其中second中包含一个block对象,而block的实现是在first中实现(一般的block传值都是这么做)。而界面的推送是由first控制器push出second控制器。但当我second控制器pop的时候,问题出现second控制器不走delloc方法,即pop后second控制器还存在没有被销毁(因为当时要做delloc中做一些操作,才发现这个问题)!block示例代码如下:

first控制器中block的实现

1 SecondViewController *secondVC = [[SecondViewController alloc] init];
2 secondVC .block = ^(NSString *text){
3         self.text = secondVC.text;
4 };

  这么一个简单的传值block使用,居然能引起second控制器无法释放,于是研究其原理,并网上搜索资料,得出一个结论:second控制器在block中被持有一次才导致其无法释放。因为block本质上是一个函数,而编译器不知道你什么时候会调用block里面的值,所以为了确保编译器内secondVC不会被释放,编译器会自动对其进行一次持有(在自身类中使用block方法操作自身的成员属性也会使自己的引用计算加1,造成无法释放)。

其解决办法也简单 在外部添加一个弱引用对象指向需要在block中操作的对象,即__weak typeof(对象名) 别名= 对象名;

1 SecondViewController *secondVC = [[SecondViewController alloc] init];
2 __weak typeof(secondVC) second = secondVC;
3 __weak typeof(self) vc = self;
4 2 secondVC .block = ^(NSString *text){
5 3         vc.text = second.text;
6 4 };

这样就能够有效的防止block使用引起的内存泄露问题。

NSTimer使用问题

另外在还在项目中遇见一个关于NSTimer的使用问题。我们想到在控制器销毁时同时停止NSTimer并置为nil

1 -(void)dealloc {
2     [self.timer invalidate];
3     self.timer = nil;
4     NSLog(@"%@ dealloc", NSStringFromClass([self class]));
5 }

然而控制器被pop后并没有走此方法(又是内存泄露),由于之前出现了Block内存泄露的问题,我就想是不是因为这个_timer加载的时候对self进行了一次持有

_timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(timerUp:) userInfo:nil repeats:YES];

进行调试测验,果然是这里出了问题,因为其对控制器持有了一次。于是我想到既然这样那我干脆就在viewWillDisappear()中做个判断,如果是pop控制器,我就先设置[self.timer invalidate]操作这样控制器就会走dealloc()方法。后来再网上找资料发现了一个更简单的解决办法,即同Block的内存管理一样使用弱引用对象

1 __weak typeof(self) vc = self;
2 _timer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:vc selector:@selector(timerUp:) userInfo:nil repeats:YES];

这样的解决办法就要比我之前的要简单多了,唯一需要注意的就是此处vc的作用域!

-----------------------如有转发请注明出处 ,谢谢!
原文地址:https://www.cnblogs.com/purple-sweet-pottoes/p/4641713.html