美文网首页iOS开发实战iOS学习笔记
你的内存泄漏了吗?几个容易被忽视的保留环

你的内存泄漏了吗?几个容易被忽视的保留环

作者: 溪石iOS | 来源:发表于2018-12-31 12:45 被阅读26次

保留环高危地带-Block

block隐性强引用self,这个大家都有警惕了,以至于发展出规范的写法:

__weak __typeof(self)weakSelf = self;
__strong __typeof(weakSelf)strongSelf = weakSelf;

(不知道如何使用weakSelf的请后面留言)
所以现在已经较少出错了,本篇列举是一些大家开发中意识不到的地方,各位iOSer可以对照检查自己的项目,看看是否引发了相同的保留环:

NSTimer 强引用

NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(timerEvent:) userInfo:nil repeats:YES];

如上面代码,这个timer并没有被self引用,那么为什么self不会被释放呢?因为timer被加到了runloop中(timer必须加到Runloop的原因),timer又强引用了self,所以timer一直存在的话,self也不会释放:

NSTimer强引用self
严格来说,这不是一个保留环,而是一个隐性的强引用链。
解决的方法是找个地方释放timer:
- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    [self.timer invalidate];
    self.timer = nil;
}

这里要注意repeat = YES 才会导致强引用
应该是苹果意识到了timer的潜在危险,所以iOS10以后,NSTimer提供了block方法:

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block

这样就可以使用“标准的weakSelf”来防止强引用self了,如果没有低版本兼容要求,强烈推荐使用这种方式启用timer。

CAAnimation 的 delegate 是强引用

以下代码会导致ViewController 不能被释放:

    CABasicAnimation *animation = [CABasicAnimation animationWithKeyPath:@"opacity"];
    animation.fromValue = [NSNumber numberWithFloat:0.2f];
    animation.toValue = [NSNumber numberWithFloat:1.0f];
    animation.autoreverses = YES;
    animation.removedOnCompletion = NO;
    animation.duration = 1;
    animation.repeatCount = HUGE_VALF;
    animation.delegate = self;
    
    [self.view.layer addAnimation:animation forKey:@"animation"];

原因是苹果违反了自己定下的规则:

凡是代理都要设置为weak

苹果给出的理由是 CAAnimation 的事件是异步的,必须强引用才能保证事件发生时执行事件,那么如何解决呢?
这里提供一个简单易用的方法:动画结束时,将 CAAnimation 设为 nil

- (void)animationDidStop:(CAAnimation *)anim finished:(BOOL)flag {
    [self.view.layer removeAnimationForKey:@"animation"];
    anim = nil;
}

另外可以使用NSProxy等第三方代理来解决,请根据自己的实际需要选择。

NSArray 强引用成员

考虑以下场景,有一组QQ用户和一组QQ群,一个用户可以加到几个群中,群当然也包含很多成员,设计用户和群的类如下:

@class Group;
@interface Person : NSObject
@property(nonatomic, strong) NSMutableArray<Group *> *myGroups;
@end
@class Person;
@interface Group : NSObject
@property(nonatomic, strong) NSMutableArray<Person *> *persons;
@end

加群的过程如下

Group *group = [[Group alloc] init];
Person *person = [[Person alloc] init];
    
person.myGroups = [NSMutableArray array];
[person.myGroups addObject:group];
    
group.persons = [NSMutableArray array];
[group.persons addObject:person];

这时候,person 和 group 都不能被释放,也无法将myGroups 或 persons 声明为weak来打破循环,因为这样数组一旦分配就会被立即释放,也就起不了保存对象的作用了,这下怎么办呢?
我们需要找个中介者,可以使用 NSValue,打破互相引用的状态:

[person.myGroups addObject:[NSValue valueWithNonretainedObject:(id)group]];

[group.persons addObject:[NSValue valueWithNonretainedObject:(id)person]];

Storyboard 上的按钮等控件要声明为 weak

正确的写法如下:

@property(nonatomic, weak) IBOutlet UIButton *btn;

从Storyboard拖出控件声明时,系统会自动声明为weak,但有时自己手动声明时候要注意检查。

小结:

本篇列举了4种容易忽视的强引用导致的内存泄漏:

  1. NSTimer的target
  2. CAAnimation的delegate
  3. NSArray数组成员
  4. Storyboard的IBOutlet

以上情形都分别给出了简便的解决方案,当然都不是唯一的方法,只要能在适当的时机打破强引用都是可以考虑的,如果你有更“优雅”的解决方案,欢迎在留言写出使用情形和具体解决方案供大家一同学习。

🌹🌹🌹赠人玫瑰,手有余香。🌹🌹🌹

相关文章

  • 你的内存泄漏了吗?几个容易被忽视的保留环

    保留环高危地带-Block block隐性强引用self,这个大家都有警惕了,以至于发展出规范的写法: (不知道如...

  • 保留环问题

    几个对象相互引用,形成保留环.由于OC内存管理模型使用引用计数构架,多以这种情况通常会照成内存泄漏.这样的话,环里...

  • 52个有效方法(33) - 以弱引用避免保留环

    保留环也就是我们常说的循环引用。保留环通常会造成内存泄漏。 避免保留环的最佳方式就是弱引用。这种引用经常用来表示“...

  • 性能优化与保活

    ------内存泄漏优化------Android 内存优化你的 Handler 内存泄露 了吗?Android卡...

  • Android 中常见的内存泄漏总结

    在 Android 开发中,稍有不慎就容易引起内存泄漏,我们经常听到内存泄漏,但是什么是内存泄漏呢? 内存泄漏:无...

  • Android 内存泄漏(MemoryLeak)

    本片文章我们从以下几个方面来了解内存泄漏: 1、什么是内存泄漏 2、内存泄漏产生的原因有哪些 3、如何定位内存泄漏...

  • 三个方法帮助解决Android内存泄漏问题

    三个方法帮助解决Android内存泄漏问题 最近自己遇到了好几个内存泄漏的问题,也帮同事解决了几个内存泄漏的问题记...

  • VideoView的内存泄漏问题

    解决内存泄漏之路 一、在XML文件直接用VideoView控件时,很容易造成内存泄漏,最开始出现的内存泄漏如下 谷...

  • Android性能优化之内存篇

    android的内存优化一般从以下几个方面考虑: 内存泄漏 内存抖动 Bitmap 代码质量优化 内存泄漏 内存泄...

  • Android 内存优化

    Android内存泄露容易导致内存溢出,又称为OOM。 内存泄漏怎么产生的 资源对象没关闭造成的内存泄漏 构造Ad...

网友评论

    本文标题:你的内存泄漏了吗?几个容易被忽视的保留环

    本文链接:https://www.haomeiwen.com/subject/cscvlqtx.html