iOS跟踪内存分配和循环引用
概述
我们在开发iOS应用期间,可能有时候就会遇到循环引用,或者内存分配状态等信息的问题,那么有没有办法可以在应用运行期间检测到了?答案是:当然有了!那就是开源的 FBMemoryProfiler ,由Facebook提供。使用起来也是非常的简单,下面我就讲解下
FBMemoryProfiler
FBMemoryProfiler是一个在应用运行期间也可以浏览内存使用情况,专为iOS开发者设计的,基本包含FBAllocationTracker和FBRetainCycleDetector。
FBAllocationTracker
是收集对象的信息,也支持生成和检测循环引用。
一。FBMemoryProfiler的安装与使用
跟踪与现实内存使用情况
1.1 安装
使用Carthage
,那么你需要在项目的Cartfile
文件里添加上代码如下:1
github "facebook/FBMemoryProfiler"
FBMemoryProfiler
需要在非debug(non-debug)模式下编译,所以你需要键入如下命令:1
carthage update --configuration Debug
使用CocoaPods
,那么需要你在Podfile
文件里添加如下代码:1
pod 'FBMemoryProfiler'
然后,在Terminal
里cd
到项目目录,最后键入pod install
这个你完全可以在Debug
模式下编译,这是有该编译标签控制的。
1.2 使用
1.在main.m
文件里添加如下代码,意味着开启FBAllocationTracker
1
2
3
4
5
6
7
8
9
10
11#import <UIKit/UIKit.h>
#import "AppDelegate.h"
#import <FBAllocationTracker/FBAllocationTrackerManager.h>
int main(int argc, char * argv[]) {
[[FBAllocationTrackerManager sharedManager] startTrackingAllocations];
[[FBAllocationTrackerManager sharedManager] enableGenerations];
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
2.开启内存占用剖析检测功能1
2
3
4
5
6#import <FBMemoryProfiler/FBMemoryProfiler.h>
FBMemoryProfiler *memoryProfiler = [FBMemoryProfiler new];
[memoryProfiler enable];
_memoryProfiler = memoryProfiler;
FBMemoryProfiler
一般是你需要在哪个控制器检测,你就把上面这块代码放到哪个控制器;我的建议是放到所有的控制器的父控制器(父控制器是你自定义的类,你所有使用带控制器的类就会从这个类继承),这比较方便,所有的控制器都可以检测到。
运行你的程序后,你就可以看到界面上有一个按钮,点击按钮,你就能看到内存使用情况了。
来看下效果
二。FBAllocationTracker的安装与使用
跟踪Objective-C对象的分配状态
2.1 安装
使用Carthage
,那么你需要在项目的Cartfile
文件里添加上代码如下:1
github "facebook/FBAllocationTracker"
FBAllocationTracker
需要在非debug(non-debug)模式下编译,所以你需要键入如下命令:1
carthage update --configuration Debug
使用CocoaPods
,那么需要你在Podfile
文件里添加如下代码:1
pod 'FBAllocationTracker'
然后,在Terminal
里cd
到项目目录,最后键入pod install
这个你完全可以在Debug
模式下编译,这是有该编译标签控制的。
这个其实在安装FBMemoryProfiler的时候就安装好了
2.2 使用
其实如果你在第一步已经配置了如下代码,那么这一步就不需要在添加了1
2
3
4
5
6
7
8
9#import <FBAllocationTracker/FBAllocationTrackerManager.h>
int main(int argc, char * argv[]) {
[[FBAllocationTrackerManager sharedManager] startTrackingAllocations];
[[FBAllocationTrackerManager sharedManager] enableGenerations];
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
现在我来解释下FBAllocationTracker
的两种模式:分别是tracking objects
和counting allocs/deallocs
,也就是跟踪对象
和分配与释放计数
。tracking objects
比较有意思但是这里不做解释了。counting allocs/deallocs
意思是当你想使用这个功能并且统计,而且不想影响性能时,你就可以使用这个模式。startTrackingAllocations
方法负责替换NSObject's
的+alloc
和-dealloc
方法,当启用enableGenerations
方法时,会开始跟踪实际对象实例数。
当然,我们也可以来抓取一下该应用的所有的类的分配情况:1
NSArray<FBAllocationTrackerSummary *> *summaries = [[FBAllocationTrackerManager sharedManager] currentAllocationSummary];
FBAllocationTrackerSummary
会告诉你,在你指定的类里,还有多少个存活的实例对象。
当我们指定一个类,并且要得知该类有多少个活着的实例对象时,可以使用如下代码1
NSArray *instances =[[FBAllocationTrackerManager sharedManager] instancesOfClasses:@[[ViewController class]]];
我们也可以查看FBAllocationTrackerManager头文件来得知更多的功能。
三。FBRetainCycleDetector的安装与使用
FBRetainCycleDetector是用来检测在iOS应用运行期间出现的循环引用问题。循环应用(Retain Cycles)是比较经典的问题,因为它会引起内存泄露。随着业务的增加,代码的复杂度也随着增加了,那么有时候要从代码中找出来哪一行代码引起了循环引用,这是一个头疼的问题,但是了?FBRetainCycleDetector就是来解决这个问题的。
3.1 安装
安装很简单,跟上面的一样,如果安装了FBMemoryProfiler
,那它会把这三个库一并安装上。
3.2 使用
3.3.1 检测循环引用
比较简单1
2
3
4
5
6
7//导入头文件
#import <FBRetainCycleDetector/FBRetainCycleDetector.h>
FBRetainCycleDetector *detector = [FBRetainCycleDetector new];
[detector addCandidate:myObject];
NSSet *retainCycles = [detector findRetainCycles];
NSLog(@"%@", retainCycles);
myObject
就是你想要跟踪的实例对象。retainCycles
就是返回循环引用对象的个数,及每一条循环应用的路径
3.3.2 过滤循环引用对象
过滤掉你不想检测到的循环引用对象,因为并不是每个循环应用都是内存泄露,代码如下1
2
3
4
5
6
7
8
9
10
11NSMutableArray *filters = @[
FBFilterBlockWithObjectIvarRelation([UIView class], @"_subviewCache"),
];
FBObjectGraphConfiguration *configuration =
[[FBObjectGraphConfiguration alloc] initWithFilterBlocks:filters
shouldInspectTimers:YES];
FBRetainCycleDetector *detector = [[FBRetainCycleDetector alloc] initWithConfiguration:configuration];
[detector addCandidate:myObject];
NSSet *retainCycles = [detector findRetainCycles];
NSLog(@"%@", retainCycles);
每一个过滤器其实是一个block,每个block有两个FBObjectiveCGraphElement
对象,可以说他们的关系是有效的。
3.3.3 objc_setAssociatedObject
Objective-C允许我们对类进行动态的添加成员变量,可以通过objc_setAssociatedObject
方法。那么当我们使用OBJC_ASSOCIATION_RETAIN_NONATOMIC
策略时,有可能这些方法也会引起循环引用。FBRetainCycleDetector
也可以捕获到这样的循环引用,但是,我们需要多一步设置,代码如下:1
2
3
4
5
6
7
8#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_setAssociatedObject
和objc_resetAssociatedObjects
,并且跟踪它们的引用
好了,就这么多了。。。
相关链接
FBMemoryProfiler
FBAllocationTracker
FBRetainCycleDetector
fishhook