這還得從使用aspects這個庫說起,如下圖:

上圖中的id token 的類型如下圖:
aspectidentifier 的block屬性對應的是圖一的傳參usingblock,很明顯token引用了usingblock,usingblock引用了token,構成循環引用,這個是一個正常的循環引用記憶體洩漏。可是fbretaincycledetector沒有掃描出環,一開始我懷疑是我自己改造過的allocationtracker沒有跟蹤到aspectidentifier執行個體(閑魚改造了fballocationtracker),經排查allocationtracker是有記錄的,可是為什麼沒有掃描出環呢?
1.記錄下運作過程中建立和釋放的執行個體
2.分析未釋放的執行個體retain了哪些執行個體(對應深度優先周遊的臨接表),逐層展開,形成一個有向圖
3.使用深度優先周遊圖的過程中,檢視是否有成環
一般的oc對象擷取retain的執行個體通過運作時接口很容易就可以擷取到,block就比較特殊,下面介紹一下fbretaincycledetector 如何擷取block捕獲的變量,并确定哪些變量是被block強引用的:
對應的接口 static nsindexset _getblockstronglayout(void block)
判斷block是否強引用其他變量并拷貝至堆上:
block引用了 1) c++ 棧上對象 2)oc對象 3) 其他block對象 4) __block修飾的變量,并被拷貝至堆上時則需要copy/dispose輔助函數,輔助函數由編譯器生成
除了case 1,其他三種case都會分别調用下面的函數:
void _block_object_assign(void destaddr, const void object, const int flags);
void _block_object_dispose(const void *object, const int flags);
_block_object_assign(或者_block_object_dispose)會根據flags的值來決定調用相應類型的copy helper(或者dispose helper)
現在回歸最開始那個問題,從圖一可以知道usingblock捕獲了3個變量,
隻有disposal、guidehandler,沒有__block id token
對比一下三者的差別,隻有token是__block聲明過(這裡已經确定token沒有被釋放,檢視allocationtracker記錄是否有token變量即可),說明fb擷取block捕獲變量的方案對__block無效
這是我聲明的一個block:
對其執行clang -rewrite-objc編譯轉換成c++實作
從上圖可以确定block捕獲的變量就存放在block的abi struct 的末尾;__block聲明的變量要複雜一些,isa、forwarding、flags、size是必有,disposer、copy是obj-c對象才有
來看看block的disposer和copy函數實作:
分别對捕獲的變量做了相應的disposer和copy處理,第二個參數傳人的值是不一樣的,估計就是這個參數有關聯
看一下紅色框:這裡直接跳過了block的頭部,做了一個小優化,直接從存放變量的索引開始
1.是否是malloc指針:不做這步過濾,會導緻指針的強轉取值crash,因為block可能捕獲了一個inter資料,這時如果當成指針來處理,那麼基本上會被當作通路非法指針而崩潰
2.去掉__block變量沒有disposer和copy 函數的變量,這個使用size大小來判斷即可
上面每一個構造指派都是必不可少的
diposer() 釋放了 struct blockbyref 下的void *refobj對象(即fbblockstrongrelationdetector對象)
2.這裡注意一下,為什麼沒有對malloc出來的的__block變量進行free?這裡調用free的話,會導緻crash,因為block的disposer函數調用到了__block變量自己的disposer接口的時候,已經釋放了這段記憶體,我是通過打開malloc scribble 這個調試開關,發現這段記憶體有部分被置為了0x55确定的。
從圖檔可以看出來__block變量已然被解析出來了
我把修改送出到fbretaincycledetector的github上的一個個人分支,等待fb的省核,附上git位址:
<a href="https://github.com/chaokongzwp/fbretaincycledetector.git">https://github.com/chaokongzwp/fbretaincycledetector.git</a>