原文地址:http://ifujun.com/fbmemoryPRofiler-shi-yong-ji-chu-jiao-cheng/
FBMemoryProfiler是Facebook開源的一款用于分析iOS內存使用和檢測循環引用的工具庫。
在上一篇[譯文]在iOS上自動檢測內存泄露中,Facebook講解了FBMemoryProfiler的核心原理。鑒于有人在評論里問怎么使用,我這里就簡單介紹一下。
建議使用Cocoapods安裝,只需要在pod文件中,添加這么一句就行:
pod 'FBMemoryProfiler'FBMemoryProfiler
最低支持iOS8,所以如果你的pod文件上最低要求是6或者7的話,是無法直接安裝的。所以,建議在開發分支或者如果你有多個target的話,在開發的target上添加FBMemoryProfiler
,在生產的target上不需要添加FBMemoryProfiler
。
我一般習慣于有兩個target,一個用于開發,里面可能會包含Reveal
、蒲公英等的庫,而這在生產包中是不必要的,另一個用于生產,只用于打生產包。
所以我的pod文件可能是這樣的:
# Uncomment this line to define a global platform for your projectplatform :ios, '8.0'# Uncomment this line if you're using Swift# use_frameworks!target 'FBMemoryProfilerTest' doendtarget 'FBMemoryProfilerTest_Dev' dopod 'FBMemoryProfiler'end
安裝成功之后,打開對于的.xcworkspace
文件即可。
Carthage
如果你的app從iOS8開始支持的話,你可以使用Carthage
來安裝。
在創建的Cartfile
文件中添加:
github "facebook/FBMemoryProfiler"之后,運行carthage update --configuration Debug
即可。
因為我的app要從iOS6開始支持,所以我沒有使用這個。
嵌入代碼
首先,要在main.m
中添加FBRetainCycleDetector
的hook,同時,也要開啟FBAllocationTracker
的生成追蹤:
#import <UIKit/UIKit.h>#import "AppDelegate.h"#if DEBUG#import <FBAllocationTracker/FBAllocationTrackerManager.h>#import <FBRetainCycleDetector/FBRetainCycleDetector.h>#endifint main(int argc, char * argv[]) { @autoreleasepool {#if DEBUG [FBAssociationManager hook]; [[FBAllocationTrackerManager sharedManager] startTrackingAllocations]; [[FBAllocationTrackerManager sharedManager] enableGenerations];#endif return UIapplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); }}我習慣于添加一個DEBUG標識符,只在Debug狀態才開啟FBMemoryProfiler
。當然,你可以不需要。
之后,我們要在AppDelegate.m
的application: didFinishLaunchingWithOptions:
中嵌入FBMemoryProfiler
的創建代碼:
#if DEBUG#import <FBMemoryProfiler/FBMemoryProfiler.h>#import <FBRetainCycleDetector/FBRetainCycleDetector.h>#import "CacheCleanerPlugin.h"#import "RetainCycleLoggerPlugin.h"#endif@interface AppDelegate (){#if DEBUG FBMemoryProfiler *memoryProfiler;#endif}@end@implementation AppDelegate- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {#if DEBUG memoryProfiler = [[FBMemoryProfiler alloc] initWithPlugins:@[[CacheCleanerPlugin new], [RetainCycleLoggerPlugin new]] retainCycleDetectorConfiguration:nil]; [memoryProfiler enable];#endif return YES;}@end其中,插件是可以不開啟的,如果你不想開啟的話,你可以這么寫:
FBMemoryProfiler *memoryProfiler = [FBMemoryProfiler new];[memoryProfiler enable];插件主要是用來進行過濾、去重或者輸出、存儲等操作的,畢竟如果不開啟插件的話,只能通過在手機、模擬器上點擊屏幕來看內存泄露,而如果自定義log插件的話,可以將捕獲到的內存泄露輸出到控制臺或者文件中。
比如說,我們可以自定義一個RetainCycleLoggerPlugin
,使用FBMemoryProfilerPluggable
協議,重寫memoryProfilerDidFindRetainCycles:
方法:
- (void)memoryProfilerDidFindRetainCycles:(NSSet *)retainCycles{ if (retainCycles.count > 0) { NSLog(@"/nretainCycles = /n%@", retainCycles); }}當FBRetainCycleDetector
找到循環引用之后,就會調用到上面的方法,但是,retainCycles
可能是個空集合,所以這里可以過濾一下。
我在測試我的app的時候,發現這樣一個問題:

我確信這里沒有因為我而導致的循環引用,但是FBRetainCycleDetector
在這里檢測到了這個環,這里的主要問題在于_subviewCache
,這是蘋果的機制,但是并不會造成內存泄露。對于這種情況,我們需要將它過濾出去。
除此之外,還有一個Timer
的問題,因為一般情況下,Timer
會強引用target,所以可能導致內存泄露,如果你確信沒有問題的話,可以關閉對Timer
的檢測。

過濾代碼類似于這種:
NSArray *filters = @[FBFilterBlockWithObjectIvarRelation([UIView class], @"_subviewCache")];FBObjectGraphConfiguration *configuration =[[FBObjectGraphConfiguration alloc] initWithFilterBlocks:filters shouldInspectTimers:YES];memoryProfiler = [[FBMemoryProfiler alloc] initWithPlugins:@[[CacheCleanerPlugin new], [RetainCycleLoggerPlugin new]] retainCycleDetectorConfiguration:configuration];[memoryProfiler enable];我們只需要設置一個過濾數組,然后添加到FBMemoryProfiler
的Configuration
中即可。
對于你確信沒有問題或者不想修改的問題,你可以在Configuration
中直接過濾掉。
比如:
NSArray *filters = @[FBFilterBlockWithObjectIvarRelation([UipanGestureRecognizer class], @"_internalActiveTouches")];如果你有CI的需要,你可以在代理中輸出log到文本,之后傳出到服務器上。
運行
具體操作可以參考Facebook的視頻,上一篇譯文中也有給出。
新聞熱點
疑難解答