iOS之block和內(nèi)存那些事

block的一些基本使用阱洪,我已經(jīng)寫了一篇,有興趣的話也可以去看看比庄,block在開發(fā)中給我們帶了很多方便豫缨,然而使用block也有一些隱患,例如容易出現(xiàn)循環(huán)引用導(dǎo)致內(nèi)存泄露先嬉,這篇文章會剖析為什么會出現(xiàn)這種問題,如何去解決,各個對象在內(nèi)存中釋放的時機等一些問題应狱。

1.為什么會出現(xiàn)循環(huán)引用

解釋這個問題我會用一個小例子和圖示分析,請看事例:

@implementation RequestUtil
- (void)getRequestData{ 
    [self.requester startWithCompletionHandler:^(NSData *data){
        _fetchData = data;
    }];
}
@end

假設(shè)requester和fetchData是RequestUtil這個類的兩個屬性祠丝,這段代碼片段貌似完全沒問題疾呻,然而它已經(jīng)出現(xiàn)循環(huán)引用了,那么上面的這段代碼內(nèi)存關(guān)系圖會是下面圖示的關(guān)系:


對象self強引用著對象requester,requester執(zhí)行startWithCompletionHandler方法的時候写半,會對block進行強引用岸蜗,在block中要訪問對象self中的屬性,首先得對對象self強引用叠蝇,只有引用了self才能訪問對象self中的屬性璃岳,這樣一來就出現(xiàn)一個閉環(huán),誰也無法釋放悔捶,這塊內(nèi)存一直被占用著铃慷,從而導(dǎo)致內(nèi)存泄露。

2. 如何解決循環(huán)引用

解決循環(huán)引用的思路是打破上圖所示的閉環(huán)蜕该,下面介紹兩種方法解決這個問題犁柜。

1.解決方案一:
@implementation RequestUtil
- (void)getRequestData{
    __weak typeof(self) weakSelf = self;
    [self.requester startWithCompletionHandler:^(NSData *data{
        weakSelf.fetchData = data;
    }];
}
@end

上面這段代碼就完美的解決了循環(huán)引用的問題,這段代碼在內(nèi)存中的關(guān)系如圖:



通過創(chuàng)建一個weak類型的指針指向self對象堂淡,block去強引用這個weakSelf就不會出現(xiàn)循環(huán)引用了馋缅。在內(nèi)存中釋放的順序是,先self釋放绢淀,self釋放由于沒有任何對象強引用requester萤悴,requester隨著釋放,之后block釋放皆的,weakSelf也沒有任何對象引用覆履,也從內(nèi)存中釋放。

2.解決方案二:

在合適的地方,手動將block或者requester釋放掉硝全,打破閉環(huán)怪嫌。

  • 手動釋放requester
@implementation RequestUtil
- (void)getRequestData{
    [self.requester startWithCompletionHandler:^(NSData *data){
        _fetchData = data;
        _requester = nil;
    }];
}
@end

上面這段代碼解決了循環(huán)引用的問題,這段代碼在內(nèi)存中的關(guān)系如圖:



當(dāng)requester指向nil的時候柳沙,它所引用的block由于沒有任何對象引用它岩灭,block會在內(nèi)存中被釋放掉,block被釋放后不再強引用self赂鲤,當(dāng)沒有其它指針強引用self的時候噪径,self會被釋放,requester也就隨著釋放数初,從而都從內(nèi)存中釋放了找爱。

  • 手動釋放blcok
@implementation RequestUtil
- (void)getRequestData{
    [self.requester startWithCompletionHandler:^(NSData *data){
        _fetchData = data;
    }];
}
@end

@implementation XXXXXXX
- (void)startWithCompletionHandler:(void (^)(NSData *data)) block{
    // do something 
    NSData *pngData = UIImagePNGRepresentation([UIImage imageNamed:@"pngName"]);
    block(pngData);
    block = nil; // 一定要確保block執(zhí)行完畢才可這么做,如果block中又有多線程泡孩,那么這么做就不合適了车摄,總之block要在合適的時間釋放
}
@end

上面這段代碼也解決了循環(huán)引用的問題,這段代碼在內(nèi)存中的關(guān)系如圖:



和手動釋放requester一個道理仑鸥,將block指向nil吮播,self會在需要釋放的時候釋放,requester也隨著釋放眼俊,從而都從內(nèi)存中釋放了意狠。

3.棧塊和堆塊

定義塊的時候,其所占的內(nèi)存是分配在棧區(qū)的疮胖,也就是說环戈,塊只在定義它的那個范圍有效。請看事例:

void (^block)();
if(flag){
    block = ^{ // do something };
} else {
    block = ^{ // do something };
}
block();

這段代碼看上去似乎很OK澎灸,其實是有風(fēng)險的院塞,定義在if和else中的兩個block都分配在棧內(nèi)存中,等離開了相應(yīng)的if語句塊或else語句塊范圍之后性昭,編譯器有可能把分配給block的內(nèi)存覆寫掉拦止。于是,這兩個塊只能保證在對應(yīng)的if或else語句范圍內(nèi)有效巩梢。這樣的代碼編譯完全沒問題的创泄,運行起來就看人品了艺玲,若編譯器未覆寫block在棧區(qū)所占用的內(nèi)存括蝠,則程序可以正常運行,若覆寫或回收了程序會崩潰饭聚。
解決辦法是給塊發(fā)送copy消息忌警,使之拷貝到堆內(nèi)存中,這樣塊就成了帶引用計數(shù)的對象了。這也是為什么block要用copy修飾的原因

void (^block)();
if(flag){
    block = [^{ // do something } copy];
} else {
    block = [^{ // do something } copy];
}
block();

這樣代碼就安全了法绵,如果是采用非ARC的箕速,那么最后記得手動釋放塊在堆區(qū)所分配的內(nèi)存。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末朋譬,一起剝皮案震驚了整個濱河市盐茎,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌徙赢,老刑警劉巖字柠,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異狡赐,居然都是意外死亡窑业,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門枕屉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來常柄,“玉大人,你說我怎么就攤上這事搀擂∥髋耍” “怎么了?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵哨颂,是天一觀的道長秸架。 經(jīng)常有香客問我,道長咆蒿,這世上最難降的妖魔是什么东抹? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任,我火速辦了婚禮沃测,結(jié)果婚禮上缭黔,老公的妹妹穿的比我還像新娘。我一直安慰自己蒂破,他們只是感情好馏谨,可當(dāng)我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著附迷,像睡著了一般惧互。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上喇伯,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天喊儡,我揣著相機與錄音,去河邊找鬼稻据。 笑死艾猜,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播匆赃,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼淤毛,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了算柳?” 一聲冷哼從身側(cè)響起低淡,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎瞬项,沒想到半個月后查牌,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡滥壕,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年纸颜,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片绎橘。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡胁孙,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出称鳞,到底是詐尸還是另有隱情涮较,我是刑警寧澤,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布冈止,位于F島的核電站狂票,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏熙暴。R本人自食惡果不足惜闺属,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望周霉。 院中可真熱鬧掂器,春花似錦、人聲如沸俱箱。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽狞谱。三九已至乃摹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間跟衅,已是汗流浹背孵睬。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留与斤,地道東北人肪康。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像撩穿,于是被迫代替她去往敵國和親磷支。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容