×

使用FBRetainCycleDetector检测引用循环

96
戴维营教育
2016.05.14 09:38* 字数 511

在iOS开发中,对象之间形成引用循环是一个很大的问题,它会让内存无故被占,甚至还有可能影响通知的接收等。我们在写代码的时候各种小心,但有时候还是避免不了掉入陷阱。下面是几种常见的循环引用示例:

1 . 两个对象通过强引用类型的属性相互持有。

ClassA *aObj = [ClassA new];
ClassB *bObj = [ClassB new];

aObj.b = bObj;
bObj.a = aObj;

2 . 对象通过拥有的Block属性引用自己。

@interface DetailViewController ()
{
    void (^_handlerBlock)();
}
@end

@implementation DetailViewController

- (void)viewDidLoad {
    [super viewDidLoad];

    _handleBlock = ^{
        NSLog(@"%@", self);
    };
}
@end

这一种情况可以称得上是最隐蔽的了。因为self不一定需要明写,而是通过实例变量隐含地引入。

3 . 通过成为正在运行的定时器的Target而被持有。这种情况可能许多初学者都不太会注意到,比如在一个页面启动定时器后,喜欢在dealloc中写上定时器的invalidate方法。其实只要定时器不停止,该页面就不会销毁,更不会执行dealloc方法。

诸如此类的引用循环还有许多,一不小心就会掉入坑中。在许多时候,我们都可以使用Xcode提供的Instruments检测内存泄漏(Leaks),但这些引用循环导致的内存泄漏有时却被忽略了。为了解决这些问题,Facebook发布了一个叫FBRetainCycleDetector的工具,专门用于检测对象是否存在引用循环。它的使用非常简单,我们可以通过CocoaPods或者直接从GitHub下载源码

pod 'FBRetainCycleDetector'

这个工具能够检测指定对象的引用情况,并把所存在的引用循环中各对象和引用在终端进行打印。

#import <FBRetainCycleDetector/FBRetainCycleDetector.h>

_handlerBlock = ^{
    NSLog(@"%@", self);
};

FBRetainCycleDetector *detector = [FBRetainCycleDetector new];
[detector addCandidate:self];
NSSet *retainCycles = [detector findRetainCycles];
NSLog(@"%@", retainCycles);

打印结果类似于:

{(
        (
        "-> DetailViewController ",
        "-> _handlerBlock -> __NSMallocBlock__ "
    )
)}

很明显,DetailViewController通过_handlerBlock实例变量引用一个Block对象,而该Block又引用了DetailViewController对象。如果不存在引用循环的话,打印的结果将是空的。

iOS开发精选
Web note ad 1