iOS隨筆——初識Block和Closure的循環(huán)引用

OC中的block和swift中的閉包使得我們能夠優(yōu)雅的解決很多問題潭辈,但是其內(nèi)存釋放問題也讓像我這樣的初學者感到頭疼

1.如何查看程序中的循環(huán)引用

這里簡單的提及兩個我個人比較常用的方法(歡迎大家補充)

  • oc的dealloc中和swift的deinit中打印日志肾请,通過控制器的釋放情況判斷當前控制器中是否存在循環(huán)引用
  • Leaks冰抢,Xcode自帶的檢測循環(huán)引用的工具,簡潔實用
2.OC中的Block解析
  • 簡單的循環(huán)引用的例子
UIButton *smartButton = [UIButton buttonWithType:UIButtonTypeCustom];
smartButton.frame = CGRectMake(50, 150, 50, 50);
 [smartButton setBackgroundColor:[UIColor redColor]];
 [self.view addSubview:smartButton];
 [smartButton buttonWithBlock:^(UIButton *button) {
     [self.navigationController popViewControllerAnimated:YES];
 }];
 - (void)dealloc {
    NSLog(@"%s",__func__);
}

當我執(zhí)行按鈕方法返回上層頁面時党巾,dealloc沒有被執(zhí)行
我們可以看一下這里的內(nèi)存循環(huán)狀況
vc->smartButton->block->vc
當控制器持有block后吼拥,block內(nèi)又捕獲了控制但校,造成循環(huán)引用
那么我們可以從block->vc這里入手蕾管,使得block不再強引用控制器,打破這個循環(huán)

__weak typeof(self) weakSelf = self;
    [smartButton buttonWithBlock:^(UIButton *button) {
        [weakSelf.navigationController popViewControllerAnimated:YES];
    }];

這樣dealloc被調(diào)用看到如下打印-[TestViewController dealloc]
關(guān)于block捕獲vc的一點拓展:
在ARC環(huán)境下枷踏,對于在堆上_NSConcreteMallocBlock類型的block(即對棧上的block進行copy操作后,被復(fù)制到堆上)掰曾,會有以下特性:
(1) block內(nèi)部如果通過外面聲明的強引用來使用旭蠕,那么block內(nèi)部會自動產(chǎn)生一個強引用指向所使用的對象。
(2) block內(nèi)部如果通過外面聲明的弱引用來使用,那么block內(nèi)部會自動產(chǎn)生一個弱引用指向所使用的對象掏熬。
這也是smartButtton會捕獲vc佑稠,淺層的理解


  • 濫用__weak的情況
    很多包括我在內(nèi)的初學者,存在濫用_weak的問題孽江,遇到Block先weak一下讶坯,這是個非常不好習慣番电,相信你看到這么一份別人的代碼也會很頭疼
SmartTool *smartTool = [[SmartTool alloc] init];
 [smartTool stupidBlock:^{
     [self.navigationController popViewControllerAnimated:YES];
 }];
[UIView animateWithDuration:1.0 animations:^{
     self.view.backgroundColor = [UIColor orangeColor];
 }];
NSArray *smartArr = @[@"1"];
 [smartArr enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
     if ([obj isEqualToString:@"1"]) {
         self.view.backgroundColor = [UIColor orangeColor];
         *stop = YES;
     }
 }];

諸如此類很多代碼就算block中捕獲了控制也不會造成循環(huán)引用岗屏,因為blcok并不被vc強引用,更多的思考可以讓你的代碼更加優(yōu)雅


  • strongSelf的使用
__weak typeof(self) weakSelf = self;
 [smartButton buttonWithBlock:^(UIButton *button) {
     __strong typeof(weakSelf) strongSelf = weakSelf;
     [strongSelf doSomething];
     [strongSelf doAnotherThing];
     // ... 可能在一個block會有很多邏輯
     [strongSelf.navigationController popViewControllerAnimated:YES];
 }];

這是一個比較官方的說法漱办,如果你不用strongSelf的話这刷,執(zhí)行方法的過程中可能weakSelf被析構(gòu),從而導(dǎo)致weakSelf = nil娩井,導(dǎo)致方法不被調(diào)用暇屋,甚至crash

我們先來看一個問題,可能會困擾初學者洞辣,_strongSelf為什么不會造成循環(huán)引用咐刨,block不是也強引用的vc嘛?這和我們第一個造成循環(huán)引用的例子有何不同扬霜?

這里只要區(qū)別在于
1.直接強引用定鸟,會引用block整個生命周期,造成循環(huán)引用
2.在block內(nèi)強引用著瓶,_strongSelf會在Block內(nèi)執(zhí)行完后被釋放联予,也就是其生命周期在block執(zhí)行時


接著我們來看一下析構(gòu)的例子:

SmartTool *tool = [[SmartTool alloc] init];
 __weak SmartTool *weakTool = tool;
 tool.luckBlock = ^{
     dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
         [weakTool stupidLog];
     });
 };
 tool.luckBlock();

這里在block里面做一個簡單GCD的延時打印方法
執(zhí)行代碼發(fā)現(xiàn)[weakTool stupidLog]方法并沒有被調(diào)用,打印發(fā)現(xiàn)在GCD的block中weakTool已經(jīng)被析構(gòu)材原,為nil
由于tool為局部變量沸久,當執(zhí)行完外部代碼,tool被釋放余蟹,luckBlock弱引用tool卷胯,weakTool當執(zhí)行完luckBlock內(nèi)部邏輯后被釋放,當延遲2s后再調(diào)用weakTool時威酒,weakTool已經(jīng)為nil

此處需要強引用weakTool窑睁,就能正常執(zhí)行打印方法

tool.luckBlock = ^{
   __strong SmartTool *strongTool = weakTool;
   dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
         [strongTool stupidLog];
   });
};

對于此處的兩個block:
1.luckBlock,strongTool對于其是內(nèi)部變量兼搏,其生命周期只在luckBlock執(zhí)行時
2.GCDBlock卵慰,strongTool對于其是外部變量,GCDBlock會強引用strongTool佛呻,直到2s后[strongTool stupidLog]方法被調(diào)用后GCDBlock被銷毀裳朋,strongTool也被銷毀

有興趣的同學,可以看一下這段代碼

@property (nonatomic,copy)void(^block)();

__weak typeof(self) weakSelf = self;
self.block = ^{
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [weakSelf test];
    });
};
self.block();

  • 主動創(chuàng)建循環(huán)引用的場景
    比如以下需求,當你執(zhí)行完一個后臺任務(wù)之后鲤嫡,通知某個實例對象送挑,做相關(guān)操作,這時候你必須保證相方都存在暖眼,并在做完相關(guān)操作之后主動斷開引用
    主動斷開循環(huán)引用的例子:
AFN中惕耕,傳入Block是被AFURLSessionManagerTaskDelegate對象引用
而AFURLSessionManagerTaskDelegate被mutableTaskDelegatesKeyedByTaskIdentifier字典引用
AFN在block執(zhí)行完后,mutableTaskDelegatesKeyedByTaskIdentifier字典會移除AFURLSessionManagerTaskDelegate對象
這樣block也被釋放

避免的循環(huán)引用的總結(jié):
1.事先通過weak-strong dance 處理引用關(guān)系
2.事后在合適位置主動斷開


swift中閉包解析

閉包與Block循環(huán)引用的原理是相同的诫肠,只是語法上存在一些區(qū)別

  • weak
let smartButton = UIButton(type: .Custom);
smartButton.buttonClickWithClosure { [weak self] (button) in
      self?.doSomething
}
 self.view.addSubview(smartButton);
  • unowned
    weak 和 unowned的語法是一致的司澎,區(qū)別在于
    weak:屬性是可選的,對象銷毀時置nil
    unowned:屬性是不可選的栋豫,必須在初始化方法中初始化值挤安,類似oc中的unsafe_unretained
  • strong
let smartButton = UIButton(type: .Custom);
smartButton.buttonClickWithClosure { [weak self] (button) in
      guard let strongSelf = self else { return }
      strongSelf.doSomething
      strongSelf.doAnotherthing
}
self.view.addSubview(smartButton);

如有錯誤,希望大家即時指正
轉(zhuǎn)載請注明出處丧鸯,謝謝

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末蛤铜,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子丛肢,更是在濱河造成了極大的恐慌围肥,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,248評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蜂怎,死亡現(xiàn)場離奇詭異穆刻,居然都是意外死亡,警方通過查閱死者的電腦和手機派敷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,681評論 2 381
  • 文/潘曉璐 我一進店門蛹批,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人篮愉,你說我怎么就攤上這事腐芍。” “怎么了试躏?”我有些...
    開封第一講書人閱讀 153,443評論 0 344
  • 文/不壞的土叔 我叫張陵猪勇,是天一觀的道長。 經(jīng)常有香客問我颠蕴,道長泣刹,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,475評論 1 279
  • 正文 為了忘掉前任犀被,我火速辦了婚禮椅您,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘寡键。我一直安慰自己掀泳,他們只是感情好,可當我...
    茶點故事閱讀 64,458評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著员舵,像睡著了一般脑沿。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上马僻,一...
    開封第一講書人閱讀 49,185評論 1 284
  • 那天庄拇,我揣著相機與錄音,去河邊找鬼韭邓。 笑死措近,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的仍秤。 我是一名探鬼主播熄诡,決...
    沈念sama閱讀 38,451評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼诗力!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起我抠,我...
    開封第一講書人閱讀 37,112評論 0 261
  • 序言:老撾萬榮一對情侶失蹤苇本,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后菜拓,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體瓣窄,經(jīng)...
    沈念sama閱讀 43,609評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,083評論 2 325
  • 正文 我和宋清朗相戀三年纳鼎,在試婚紗的時候發(fā)現(xiàn)自己被綠了俺夕。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,163評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡贱鄙,死狀恐怖劝贸,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情逗宁,我是刑警寧澤映九,帶...
    沈念sama閱讀 33,803評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站瞎颗,受9級特大地震影響件甥,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜哼拔,卻給世界環(huán)境...
    茶點故事閱讀 39,357評論 3 307
  • 文/蒙蒙 一引有、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧倦逐,春花似錦譬正、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,357評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽守谓。三九已至,卻和暖如春您单,著一層夾襖步出監(jiān)牢的瞬間斋荞,已是汗流浹背恒傻。 一陣腳步聲響...
    開封第一講書人閱讀 31,590評論 1 261
  • 我被黑心中介騙來泰國打工葵萎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人受葛。 一個月前我還...
    沈念sama閱讀 45,636評論 2 355
  • 正文 我出身青樓悦陋,卻偏偏與公主長得像蜈彼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子俺驶,可洞房花燭夜當晚...
    茶點故事閱讀 42,925評論 2 344

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