你好,歡迎來到IOS教程網

 Ios教程網 >> IOS編程開發 >> IOS開發基礎 >> Facebook查找RetainCycle開源庫的分析

Facebook查找RetainCycle開源庫的分析

編輯:IOS開發基礎

1.jpg

FBRetainCycleDetector是Facebook新開源的一個項目。配合FBMemoryProfiler使用起來也是很方便。當然FBMemoryProfiler裡面使用到了FBAllocationTracker。目前第一版,在測試的過程中也會遇到一些crash,相信經過使用者的修改和作者本人的自測,會越來越完善的。這篇文章的目的主要是對於FBRetainCycleDetector內部實現進行一個介紹,單單只會使用總感覺是遠遠不夠的。 文章會分為幾個模塊進行介紹: 最簡單的使用方法 主要元素類及其輔助類的介紹 * 主要的查找類及其輔助類介紹

最簡單的使用方法

最簡單的使用方法,不包含Configuration。單純的去查找一個對象的引用循環 FBRetainCycleDetectordetector = [[FBRetainCycleDetector alloc] initWithConfiguration:nil]; [detector addCandiate:myObject]; //- (NSSet)findRetainCycles; NSSet> *retainCycles = [detector findRetainCycles]; NSLog(@"%@", retainCycles); 這裡先簡單的說明一下,findRetainCycles查詢方式所使用到的算法是DFS(深度優先搜索)。

主要元素類及其輔助類的介紹(FBObjectiveCGraphElement )

FBRetainCycleDetector所使用到的對象類型是FBObjectiveCGraphElement,會在調用函數:addCandiate的時候內部進行初始化為該對象類型或者其子類。

FBObjectiveCGraphElement

FBObjectiveCGraphElement是所有用來查找對象類型的基類。所有的查找對象都基於它實現。該類並不需要外部的調用,主要是供內部查詢使用。其提供的功能主要是: 提供初始化方法封裝object(即調用addCandiate傳入的object) 獲取所有該對象所持有對象- (NSSet *)allRetainedObjects;。基類FBObjectiveCGraphElement 所獲取的對象類型是通過associated object所持有的對象。 associated object對象的獲取是通過Facebook自身的fishhook去hook原先的objc_setAssociatedObjectobjc_removeAssociatedObjects來實現對象的持有標記。 提供過濾接口`- (NSSet )filterObjects:(nullable NSArray )objects;,過濾接口主要是與FBObjectGraphConfiguration相結合使用,FBObjectGraphConfiguration`會在下文介紹。 以及其它一些helper接口,例如:獲取類、類名、地址等等。

FBObjectGraphConfiguration

這裡先介紹一下Configuration,再去介紹FBObjectiveCGraphElement的子類。FBObjectGraphConfiguration 內容很少,其主要提供的是過濾的block類型FBGraphEdgeFilterBlock和過濾器的初始化方法: - (instancetype)initWithFilterBlocks:(NSArray)filterBlocks shouldInspectTimers:(BOOL)shouldInspectTimers 即傳入一個過濾block的數組,該數組會被FBObjectiveCGraphElement 對象類型在調用filterObjects 的時候一次調用。shouldInspectTimers 的作用是是否檢查NSTimer。
接下來看看FBGraphEdgeFilterBlock 的定義: typedef FBGraphEdgeType (^FBGraphEdgeFilterBlock)(FBObjectiveCGraphElement 
_Nullable fromObject, FBObjectiveCGraphElement _Nullable toObject); 傳入fromObject(傳入的對象)和toObject(被持有的對象),根據自己需求對對象進行處理。添加到數組後進行初始化。這裡可以舉個例子,過濾掉所有以UINavi 開頭的對象: FBGraphEdgeFilterBlock filterBlock = ^(FBObjectiveCGraphElement _Nullable fromObject, FBObjectiveCGraphElement _Nullable toObject){ if (![[fromObject classNameOrNull] hasPrefix:@"UINavi"]) { return FBGraphEdgeValid; } return FBGraphEdgeInvalid; }; FBObjectGraphConfiguration configuration = [[FBObjectGraphConfiguration alloc] initWithFilterBlocks:@[filterBlock] shouldInspectTimers:NO]; FBRetainCycleDetector *detector = [[FBRetainCycleDetector alloc] initWithConfiguration:configuration]; 這就是一個包含configuration的初始化過程。

FBObjectiveCGraphElement相關子類

上面已經說了,FBObjectiveCGraphElement只提供了對associate object的持有查找。因此其它對象的持有查找是通過子類實現的,主要包含:FBObjectiveCBlock,FBObjectiveCObject,FBObjectiveCNSCFTimer

FBObjectiveCBlock實現

主要的實現內容是:重寫父類方法allRetainedObjects,當然也是有調用[super allRetainedObjects]。接下來就是對於block的識別和獲取引用關系。最後再封裝為FBObjectiveCBlock 對象類型。
* block識別方法的一些細節:
用到FBBlockStrongLayout.h裡面的函數(用C實現)來進行判斷是不是block以及獲取引用。判斷是不是block所用的方法是利用一個空block^{},判斷傳入的block是不是其子類。當然這裡是先轉換為Class類型。

  • block內部引用關系獲取:
    獲取引用關系相對就比較發雜一點,先通過block的size_t大小創建相同大小的數組類型,其對象類型是FBBlockStrongRelationDetector,該對象主要是為了統計block內部內容是否是一個對象,會在release的時候進行標記。接下來對於每一個調用該block的dispose_helper,如若調用了release則證明其是對象,否則就是一些普通數據類型。記錄其在block內部的位置關系,返回位置關系數組。再通過數組獲取每一個對象。

  • 最後當然也會調用filterObjects過濾掉不需要查找引用循環的對象。

    FBObjectiveCObject實現

    在重寫以及調用父類方法與block是一樣的。不同的地方在於對於持有對象的獲取。。

  • FBClassStrongLayout裡面的函數輔助獲取對象,同樣利用到了runtime(class_copyIvarList)機制去獲取屬性列表,並且封裝為FBIvarReference類型。如果遇到屬性是struct類型還需單獨進行處理。

  • FBIvarReference的初始化方法會對不同的Ivar進行分類:FBObjectType,FBBlockType,FBStructType,FBUnknownType.

  • 然後也是會封裝成FBObjectiveCObject ,在未過濾之前,需要判斷該object的類型。因為object的有些類型在進行數據處理的時候會造成崩潰,目前fb處理了一部分,但經過測試還是會發生異常的崩潰現象,不過這個現象主要是對於系統的一些對象類型遍歷所造成的。暫時沒有發現自己創建的對象在查找retain cycle的時候發生崩潰。

    FBObjectiveCNSCFTimer實現

    FBObjectiveCNSCFTimer的實現內容比較少,其主要就是通過runloop去獲取CFRunLoopTimerGetContext,再對獲取到的數據進行處理即可。

    主要的查找類及其輔助類介紹(FBRetainCycleDetector)

    FBRetainCycleDetector

  • FBRetainCycleDetector 主要的功能就是查找retain cycle。使用到的算法思想是深度優先搜索(DFS),因此如果在對象量比較大並且查找深度(默認為8)比較深的情況下,會比較慢。一般情況下是在異步線程執行查找。

  • FBRetainCycleDetector 會對通過方法addCandidate所添加的對象都進行DFS,當然查找之前會通過FBObjectGraphConfiguration進行過濾。
    其中查找過程對對象會進一步的封裝為FBNodeEnumerator 類型,接下來介紹該類型。

    FBNodeEnumerator

  • FBNodeEnumerator繼承於NSEnumeratorNSEnumerator可以方便的提供nextObject的方法調用,只需在子類中重寫該方法即可。

  • FBNodeEnumerator中的nextObject主要的處理是:通過object去獲取allRetainedObjects(此方法是FBObjectiveCGraphElement提供獲取過濾後的持有對象)。再獲取第一個對象進行返回。

  • 至於深搜的一些數據存儲,這裡就不進行解釋。

    結論

    FBRetainCycleDetector目前處於第一版本,因此會有一些bug,但並不會影響正常的使用。雖然查找算法上面有可能會導致比較大的內存消耗(畢竟如果程序夠大的話,深搜也是談不上效率的)。暫時沒有對FBMemoryProfiler 進行描述的原因是,FBMemoryProfiler 主要還是界面的實現以及與FBAllocationTracker 功能的結合。 FBAllocationTracker 的功能比較簡單,後面會用一篇小文章來進行概述。

  1. 上一頁:
  2. 下一頁: