ARC下的内存泄漏

总结一下在ARC下的内存泄漏问题

ARC全称叫 ARC(Automatic Reference Counting)。在编译期间,编译器会判断对象的使用情况,并适当的加上retain和release,使得对象的内存被合理的管理。所以,从本质上说ARC和MRC在本质上是一样的,都是通过引用计数的内存管理方式。ARC 的出现大大节省了程序员手动管理内存的时间成本,But,世上没有完美的事物,我们也不要把任何事想的那么美好,在 ARC 环境下如果不注意的话也会引起内存泄漏。

###循环引用(Retain Cycle)

这里分三种情况讨论:

  • Delegate

    我们在使用代理设计模式的时候,一定要注意将 delegate 变量声明为 weak 类型,像这样 @property (nonatomic, weak) id delegate;

  • Block

    Block 循环引用也很常见,假设 self 代表当前 ViewController,那么一般写法是这样:

    1
    2
    3
    4
      __weak typeof(self)weakSelf = self;
    self.completionHandler = ^{
    NSLog(@"%@", weakSelf);
    };

当然 Block 循环引用其实有很多东西可以讲,我后边会专门写一篇来介绍 Block。

  • NSTimer

    + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;
    如果在一个 ViewController 里创建了一个定时器,并且 repeats:值为 YES,一定记得在 pop/dismiss 当前 ViewController 将 timer 设为 invalidate,否则 会造成循环引用,这里要特别需要 注意 的一点是:我们不要在 ViewController 的 dealloc 方法里调用 [timer invalidate]; 因为从来不会调到这里,我们应该在 viewWillDisappear里边调用,像这样:

    1
    2
    3
    4
    5
    6
    - (void)viewWillDisappear:(BOOL)animated{
    [super viewWillDisappear:animated];
    if ([self isMovingFromParentViewController]) {
    [timer invalidate]; //解除对self的引用
    }
    }

CoreFundation/CoreGraphics 相关函数

ARC 针对的 Objective-C 对象来说的,而 CoreFundation/CoreGraphics 这种非Objective-C对象,我们一定要记得自己手动释放,否则会造成内存泄漏

比如:

CGBitmapContextCreate(, , , , , , )

一定要与

CGImageRelease(CGImageRef image)
成对出现。

只要记住一点:CG/CF 开头的函数,有创建就要有释放,这样可以有效降低内存泄漏的风险。

如何检测内存泄漏

开发应用程序,内存泄漏不可避免,那么如何检测内存泄漏呢,那就要说道 Xcode工具套件中的 Instruments。针对 内存泄漏,主要是两个 AllocationsLeaks

这里有篇教程写的比较详细:http://www.raywenderlich.com/23037/how-to-use-instruments-in-xcode
我建议大家直接看英文原版吧,也不难。这里主要说明几点:

  • 教程中用的 Instruments 界面跟目前最新的略微有些差别,比如 里边 提到的处于左边的 Mark Heap 按钮,目前改为了 Mark Generation ,在这里:
    image

  • 文中的样例,调用的 flickr api url的开头改为 https:// ,并且需要翻墙才可以访问。

  • 对于 循环引用 如何进行检测

    简单点可以通过在对应的类中复写 dealloc 方法,像这样:

    1
    2
    3
    - (void)dealloc{
    NSLog(@"=== %@ dealloced! ===", NSStringFromClass([self class]));
    }

    如果调用这里,说明不存在循环引用。

    同样,我们也可以通过 Instruments 来检测,利用 Allocations ,如图:

    image

这里以 ViewController 为例,在搜索框中输入想要查看的类名,然后连续进行 pop/push 操作,看 #Total 那一列的个数有没有连续增加,如果连续增加,那么说明出现循环引用了,注意:此方法只适用于真机。