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

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 检测跟踪所有分配和强制引用循环。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,732评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,496评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,264评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,807评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,806评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,675评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,029评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,683评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,704评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,666评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,773评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,413评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,016评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,978评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,204评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,083评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,503评论 2 343

推荐阅读更多精彩内容

  • 出题者简介: 孙源(sunnyxx),目前就职于百度,负责百度知道 iOS 客户端的开发工作,对技术喜欢刨根问底和...
    戈多_于勒阅读 1,786评论 0 5
  • 版本记录 前言 在APP中,内存是极其宝贵的资源,但是由于程序员的代码有时候会大意而出现问题,容易引起leak,其...
    刀客传奇阅读 2,723评论 0 1
  • 第一次尝试用简书记录 哥仑布发现新大陆一样用了分隔线,图也是随手找的,注册好久了都没认真去用,好奇心也随着年华老去...
    译兮18阅读 168评论 0 0
  • 第一次在荧幕上看到吴京应该是1997年那部电视剧《太极宗师》,那是我还在上初中。记得那部电视剧的主题曲非常特别,好...
    豪杰春香阅读 192评论 0 1
  • 时光荏苒,转眼间,三十已至,忙忙碌碌中,我们早已不在年华。我们早已忘却曾经,年少,丢却梦想,早就踏上生活,中年,现...
    沙默孤狼影阅读 351评论 4 2