在 iOS 5之前使用的都是 MRC,需要程序员自己去 Retain,Release,这样一不注意会经常产生内存泄漏问题,但是之后出现了 ARC,系统会自动帮助我们在合适的位置添加 Retain,Release,可以说是减少了大部分的麻烦.但是在一些情况下还是会产生内存泄漏,需要我们注意
那么在什么情况下会产生内存泄漏呢,在 ARC 中,产生内存泄漏主要是因为循环引用,当有循环引用的时候,导致谁都不能释放,所以就会产生内存泄漏.
在我们日常开发中最常见的就是使用 block 产生的循环引用,delegate 和 NSTimer 等都会产生循环引用的问题.
首先让我们深度了解一下 block 的循环引用以及如何更好地处理:
当一个类有 block 属性,而在 block 中又引用这个类本身 self 时,就会产生循环引用导致都释放不了,形成 A -> B, B -> A两方的循环引用.
还有一种情况,当某各类有 block 属性, 控制器 viewController 强引用这个类, 在 block 中 又调用 控制器本身时,就会形成 A -> B, B -> C, C -> A,三方的循环引用. 这两种情况都属于 block 引起的循环引用,解决办法就是让一方变为弱引用,通常情况下使 block 对 self 变成弱引用来解决循环引用问题,很多人只会使用如下 __weak typedef(self) weakSelf = self; self.block = ^(){ weakSelf.view.backgroundColor = [UIColor redColor]; } 但是这样很可能会出现崩溃,因为把 self 变成弱引用后,在它的作用域中随时都可能被回收而变成 nil ,正确的做法是 __weak typedef(self) weakSelf =self; self.block = ^(){ __strong typedef(weakSelf) strongSelf = weakSelf; if(strongSelf) { strongSelf.view.backgroundColor = [UIColorredColor]; }}
看唐巧的文章中提到这么一个问题, 如果说 block 会产生循环引用,但是业务上又不能使用 weakSelf 呢?那么这种情况下应该如何处理循环引用呢? 场景: 在 YTNetwork 中,每一个网络请求 API 会持有回调的 block , block 会持有 self, self 也持有 网络请求的 API ,这时候很明显是一个循环引用 ,但是你有一个后台任务,希望任务执行完后,通知另外一个实例,这时候就需要我们主动释放 block,方法很简单,就是把 block 手动置为 nil, self.block = nil;
其次就是 NSTimer 产生的循环引用问题
那么 NSTimer 会如何产生循环引用呢? 首先 timer 定时器要加入到 runloop 中,此时 runloop 会对 timer 强引用, 而 timer 注册时又会对 targer(一般是 self 控制器) 强引用 而 self 又会对 runloop 产生强引用 ,此时就造成循环引用的问题,有时候我们我们对某个Timer的targer设置了nil。但没设置[timer invalidate].其实这个对象还是没被释放的。timer对应的执行方法也一直会在线程中执行。容易造成内存泄露。
如何解决呢,即何时调用 [timer invalidate] 方法呢,有些人可能想到在 dealloc 方法中 ,但是你别忘了, dealloc 方法什么时候调用呢 ,就是当 self 释放的时候才会调用 dealloc 方法,此时产生了循环引用就意味着 self 不会释放了 ,所以在 dealloc 方法中调用将不会执行, 我们可以考虑在 ViewWillDisAppear 方法中调用 [timer invalidate] 使定时器失效.
最后就是在 delegate 情况下产生的循环引用
这种情况大家都很熟悉, 就是代理属性需要使用 weak 修饰,就可以避免循环引用问题,这里就不详细介绍了.