内存泄漏是开发中经常会遇到和需要处理的问题,如:循环引用、僵尸对象和野指针、大循环内存峰值。
循环引用
即对象A强引用对象B,对象B强引用对象A,或者多个对象强引用形成一个闭环。block会对内部使用的对象进行强引用,因此在使用的时候应该确定不会引起循环引用。
解决方案:
使用弱引用或主动断开循环。
示例1:block
导致内存泄漏的代码(action block中的self):
self.block = ^{ |
改成这样可以正常dealloc:
__weak typeof(self) weakSelf = self; |
或者:
__block UIViewController *vc = self; |
或者:
self.block = ^(UIViewController *vc){ |
示例2:NSTimer
NSTimer会造成循环引用,timer会强引用target即self,一般self又会持有timer作为属性,这样就造成了循环引用。
那么,如果timer只作为局部变量,不把timer作为属性呢?同样释放不了,因为在加入runloop的操作中,timer被强引用。而timer作为局部变量,是无法执行invalidate的,所以在timer被invalidate之前,self也就不会被释放。
所以我们要注意,不仅仅是把timer当作实例变量的时候会造成循环引用,只要申请了timer,加入了runloop,并且target是self,虽然不是循环引用,但是self却没有释放的时机。如下方式申请的定时器,self已经无法释放了。
NSTimer *timer = [NSTimer timerWithTimeInterval:5 target:self selector:@selector(commentAnimation) userInfo:nil repeats:YES]; |
解决这种问题有几个实现方式,大家可以根据具体场景去选择:
- 增加startTimer和stopTimer方法,在合适的时机去调用,比如可以在viewDidDisappear时stopTimer,或者由这个类的调用者去设置。
- 每次任务结束时使用dispatch_after方法做延时操作。注意使用weakself,否则也会强引用self。
- (void)startAnimation
{
WS(weakSelf);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
[weakSelf commentAnimation];
});
} - 使用GCD的定时器,同样注意使用weakself。
WS(weakSelf);
timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_main_queue());
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 5 * NSEC_PER_SEC, 1 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{
[weakSelf commentAnimation];
});
dispatch_resume(timer);示例3:NSNotification
使用block的方式增加notification,引用了self,在删除notification之前,self不会被释放,与timer的场景类似,其实这段代码已经声明了weakself,但是调用_eventManger方法还是引起了循环引用。
也就是说,即使我们没有调用self方法,_xxx也会造成循环引用。
[[NSNotificationCenter defaultCenter] addObserverForName:kUserSubscribeNotification object:nil queue:nil usingBlock:^(NSNotification *note) { |
僵尸对象和野指针
僵尸对象是指内存已经被回收的对象,指向僵尸对象的指针就是野指针,向野指针发送消息会导致崩溃:
EXC_BAD_ACCESS |
解决方案:
当对象释放后,应该将其置为nil。
大循环内存峰值
循环内产生大量的临时对象,直至循环结束才释放,可能导致内存泄漏。
解决方案:
在循环中创建自己的autoreleasepool,及时释放占用内存大的临时变量,减少内存占用峰值。
for (int i = 0; i < 5000; i++) { |
使用Instruments工具检查内存泄漏

