講述Sagit.Framework解決:雙向引用導致的IOS記憶體流失(上),sagit.frameworkios
前言:
好久沒寫文章了,最近先是重構IT戀、又重寫IT戀中。
Sagit架構也不斷的更新,調整,現在感覺已完美了了相當的多。
今天不寫教程,先簡單分享一下技術內容。
1:見Block必有:#define WeakSelf __weak typeof(self) this = self;
故事要從這這裡說起:
當初番完這代碼後,發現到處都有這個鬼東西,然後就去百度了一下,然後大意是為了:
解決雙向引用導致的記憶體無法釋放問題
簡單的表述如下:
self 強引用指向=》block;block 也強引用批向=>self;
這時候就出事了,解決的方法是,把其中一個改成弱指向。
而WeakSelf的定義,就是讓block改成弱引用,這樣無論self是不是強引用的指向block都無關緊要了。當然,更精緻的做法是:先預判self有沒有強引用指向block,沒有,就不用WeakSelf定義了。不過,一般新手搞不明白內涵,無法做出有效預判,所以見block就有WeakSelf也就相隨相生了。
2:其它情境的雙向引用:UIViewController與UIView的糾纏
首先,預設UIViewController有一個強引用指向了UIView,這是系統定義的,我們改不了:
所以,如果UIView裡再出現strong或retain指回UIController,就會導致UIViewController和UIView雙雙無法釋放問題。
這個問題,在我剛寫Sagit架構時,只在意功能,沒在意這些,就犯了這個錯誤:
錯誤的寫法是這樣的:
現在改正後的寫法是這樣的:
3:事情沒有這麼簡單:UIView子控制項綁定事件指向Controller
看一行Sagit的代碼,關注後面的addClick:
[[[[sagit addButton:@"Login" title:@"登入" font:40] width:450 height:80] onBottom:@"pwdLine" y:149] addClick:@"loginClick"];
對於事件流程關於Sagit的前面幾篇有說了,這裡說一下架構的流程代碼:
1:系統自動添加了一個UITapGestureRecognizer,並指定到一個固定的click方法;2:將方法名稱和target存到自身的NSDictionary的字典中(架構為每個UIView都擴充了一個Dictionary)(就是這裡造成強引用了Controller).3:事件點擊時:先觸發系統預設的click,然後click事件:從字典裡取出方法名和target,找到SEL並動態執行。PS:設計成動態執行的好處:可以在執行前處理一些其它事情:比如將addClick參數:loginClick改成AgeButton.click,這樣可以分解參數後,去執行AgeButton上的事件
執行的代碼是這樣的,由於是動態執行,少不了還有一個警告:
接下來,就是怎麼消滅事件裡對Controller的強引用:
1:找了資料,發現有個NSMapTable,是弱引用的字典,於是把NSDictionary換成它,結果:參不忍睹,介面錯亂。【大概是弱引用特別容易遺失資料】
2:嘗試用一個全域的第三方的字典來存,結果也悲哀了!3:最後想到了一個方法,不直接存Controller,只存字串:1和0 ,在最終執行的時候,再去找。
代碼是這樣的:
真難為我這麼聰明,想著大功告成,運行,釋放了,成功了!!!
然後又悲哀了:
然後就動不動就到main含數了,讓我怎麼猜?說好的全域斷點呢?你咋不斷呢?
搜了搜百度,想想要調度記憶體,那就一個蛋騰,還是靠猜吧。
後來,根據釋放的順序,和最後的關鍵字,大概是這樣猜的:
控制器被釋放了,這時候UIView還沒釋放,然後系統又給UIView綁字的事件發訊息,結果遇到野指標,悲傷的故事發生了。
於是,我做了一個艱難的決定,在UIController的deallow中寫了這樣的代碼:
-(void)dealloc{ [self.view removeAllsubViews];//處理記憶體釋放後的異常。 NSLog(@"%@ ->UIViewControlelr relase", [self class]);}
這執行dealloc前,畢竟Controller還是活著的,這時候趕緊把UIView的東西給清了,然後,發現完美,運行起來很6!
總結:
當我很6的解決完上述問題後,就開始寫文章了想分享一下了,然後寫了開頭,發現:
咦,好像UITableView和UITableViewCell,好像也有雙向引用問題。
因為我給Cell加了個屬性,指向Table,運行,果然,Shit,連Controller和父的UIView都釋放了,你UITableView做為子UI居然不釋放!!!!
沒天理,繼續折騰,然後UITableView搞釋放了,又發現UITableViewCell不釋放了(這個Cell通常又會是一大堆UI)。
再然後,發現Push兩層回來,又掛Crash了。
現在正在全力搶救!!!解決完再來寫下篇!!!
操,發現為了釋放那點記憶體的代價,折騰起來真慘過不釋放算了〜〜〜〜