FBRetainCycleDetector

利用 Facebook 的开源库在运行时自动检测强引用循环

2017-01-18  本文已影响255人  张嘉夫

facebook / FBRetainCycleDetector

帮助在运行时检测循环引用的iOS 库

FBRetainCycleDetector

使用运行时分析找到循环应用的iOS 库。

关于

循环引用是造成内存泄露最常见的原因。要创建一个循环引用相当简单,并且往往难以发现它。FBRetainCycleDetector 的目标是帮助在运行时找到循环引用。这个项目的功能受到 [Circle]( GitHub - mikeash/Circle ) 的启发。

安装

Carthage

在你的 Cartfile 里添加:
github "facebook/FBRetainCycleDetector"
FBRetainCycleDetector是从非调适版本构建的,所以当你想测试它的时候,使用
carthage update --configuration Debug

CocoaPods

在你的 podspec 里添加:
pod 'FBRetainCycleDetector'
你只能在 Debug 构建中才能完全使用 FBRetainCycleDetector。由 [compilation flag]( FBRetainCycleDetector/FBRetainCycleDetector.h at master · facebook/FBRetainCycleDetector · GitHub ),可以提供在其它配置的构建中使用。

使用示例

让我们迅速深入
#import <FBRetainCycleDetector/FBRetainCycleDetector.h>

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

- (NSSet<NSArray<FBObjectiveCGraphElement *> *> *)findRetainCycles会返回一组包装对象的数组。刚开始看会很困难,但是可以克服。这个 set 里的每个数组都表示一个循环引用。此数组中的每个元素都是此引用循环中一个对象的包装器。参考 [FBObjectiveCGraphElement]( FBRetainCycleDetector/FBObjectiveCGraphElement.h at master · facebook/FBRetainCycleDetector · GitHub )。
输出示例如下:

{(
    (
        "-> MyObject ",
        "-> _someObject -> __NSArrayI "
    )
)}

MyObject 通过 someObject 属性引用 NSArray 同时它也是 NSArray 的一部分。
FBRetainCycleDetector 会查找不超过10个对象的循环。我们可以让它查找更多(虽然会更慢!)。

FBRetainCycleDetector *detector = [FBRetainCycleDetector new];
[detector addCandidate:myObject];
NSSet *retainCycles = [detector findRetainCyclesWithMaxCycleLength:100];

过滤器

也有我们想忽略的循环引用。因为不是每个循环引用都是泄露,所以我们想要过滤掉它们。为此,我们需要指定过滤器:

NSMutableArray *filters = @[
  FBFilterBlockWithObjectIvarRelation([UIView class], @"_subviewCache"),
];

// Configuration object can describe filters as well as some options
FBObjectGraphConfiguration *configuration =
[[FBObjectGraphConfiguration alloc] initWithFilterBlocks:filters
                                     shouldInspectTimers:YES];
FBRetainCycleDetector *detector = [[FBRetainCycleDetector alloc] initWithConfiguration:configuration];
[detector addCandidate:myObject];
NSSet *retainCycles = [detector findRetainCycles];

可以说每个过滤器是一个拥有两个 FBObjectiveCGraphElement 对象的 block,如果它们的关系有效的话。
参考 FBStandardGraphEdgeFilters 来学习更多如何使用过滤器。

NSTimer

NSTimer 可能会很麻烦,因为它会引用它的目标。通常它意味着循环引用。FBRetainCycleDetector可以检测它们,但如果你想跳过,可以在传给 FBRetainCycleDetector 的配置里指明.

FBObjectGraphConfiguration *configuration =
[[FBObjectGraphConfiguration alloc] initWithFilterBlocks:someFilters
                                     shouldInspectTimers:NO];
FBRetainCycleDetector *detector = [[FBRetainCycleDetector alloc] initWithConfiguration:configuration];

关联

Objective-C 允许用户使用 objc_setAssociatedObject 为每个对象设置关联对象。
这些关联对象可能会导致循环引用,如果我们使用 retaining policies 的话,例如 OBJC_ASSOCIATION_RETAIN_NONATOMIC。FBRetainCycleDetector 可以捕获这种类型的循环,但是需要设置一下。在应用声明周期的一开始,最好在 main.m 里我们可以这样添加:

#import <FBRetainCycleDetector/FBAssociationManager.h>

int main(int argc, char * argv[]) {
  @autoreleasepool {
    [FBAssociationManager hook];
    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
  }
}

上面的代码里 [FBAssociationManager hook] 会在关联被创建前使用 fishhook 来插入 objc_setAssociatedObjectobjc_resetAssociatedObjects 函数来追踪它们。

获得 Candidates

如果想要对 app 进行性能分析,你可能想要抽象理解 FBRetainCycleDetector 是如何获得 candidates 的。虽然你自己很简单的就可以追踪它们,但你也可以使用 FBAllocationTracker。这是我们创建的一个小工具,可以帮助你追踪对象。它提供了简单的API,您可以查询例如给定类的所有实例,或当前跟踪的所有类名等。
FBAllocationTrackerFBRetainCycleDetector 可以很好地合作。 我们创建了一个小型示例和名为 FBMemoryProfiler 的插件项目来利用这两个项目。 它提供了非常基本的UI,您可以使用它从 UI 检测跟踪所有分配和强制引用循环。

上一篇 下一篇

猜你喜欢

热点阅读