iOS引起內(nèi)存延遲釋放的N種可能和解決方法(附demo)

文章背景

在很多的時(shí)候 , 我們項(xiàng)目中的控制器會(huì)延遲釋放 ,為了了解為什么會(huì)延遲釋放和延遲釋放的解決方式特制作了一個(gè)demo, 項(xiàng)目中延遲釋放會(huì)帶來很多問題, 因?yàn)檠舆t師范項(xiàng)目中的資源清理不敢放在dealloc 中 進(jìn)行, 因?yàn)橐坏┭舆t釋放了, 可能存在已經(jīng)在viewdidload 修改的變量, 被延遲釋放的控制器將數(shù)據(jù)修改了

延遲釋放的原因

根據(jù)蘋果官方文檔中對(duì) NSAutoreleasePool 的描述毫炉,我們可知竞穷,在主線程的 NSRunLoop 對(duì)象(在系統(tǒng)級(jí)別的其他線程中應(yīng)該也是如此坷剧,比如通過 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0) 獲取到的線程)的每個(gè) event loop 開始前脑沿,系統(tǒng)會(huì)自動(dòng)創(chuàng)建一個(gè) autoreleasepool ,并在 event loop 結(jié)束時(shí) drain 麻诀。我們推出一個(gè)控制器的時(shí)候 中創(chuàng)建的 autoreleased 對(duì)象就是被系統(tǒng)添加到了這個(gè)自動(dòng)創(chuàng)建的 autoreleasepool 中,并在這個(gè) autoreleasepool 被 drain 時(shí)得到釋放。如果有定時(shí)器的任務(wù)沒有執(zhí)行完, 是不會(huì)調(diào)用drain的 , 知道定時(shí)器事件執(zhí)行完, 才會(huì)調(diào)用drain 來釋放控制器 ,如果定時(shí)器一致在行走, 對(duì)象是不釋放的, 除非使用weak

本文所述的延遲釋放情形在release 模式下也會(huì)存在延遲釋放

image.png

延遲釋放情形一)

dispatch_after , dispatch_after延時(shí)N秒, 控制器就會(huì)在N秒之后釋放

  dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    //        for(int i = 0;i< 10;i++){
    //            [weakSelf requst:@"http://forspeed.onlinedown.net/down/YJPDFViewer2.0.zip"];
    //        }
    //    });

解決方案1) - weakSelf

//      方案一  weak 下 會(huì) 可以解決延遲釋放
        __weak typeof(self) weakSelf = self;
        dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
            for(int i = 0;i< 10;i++){
                [weakSelf requst:@"http://forspeed.onlinedown.net/down/YJPDFViewer2.0.zip"];
            }
        });

解決方案2) weak strong

weak strong 的代碼

#ifndef weakify
#if DEBUG
#if __has_feature(objc_arc)
#define weakify(object) autoreleasepool{} __weak __typeof__(object) weak##_##object = object;
#else
#define weakify(object) autoreleasepool{} __block __typeof__(object) block##_##object = object;
#endif
#else
#if __has_feature(objc_arc)
#define weakify(object) try{} @finally{} {} __weak __typeof__(object) weak##_##object = object;
#else
#define weakify(object) try{} @finally{} {} __block __typeof__(object) block##_##object = object;
#endif
#endif
#endif

#ifndef strongify
#if DEBUG
#if __has_feature(objc_arc)
#define strongify(object) autoreleasepool{} __typeof__(object) object = weak##_##object;
#else
#define strongify(object) autoreleasepool{} __typeof__(object) object = block##_##object;
#endif
#else
#if __has_feature(objc_arc)
#define strongify(object) try{} @finally{} __typeof__(object) object = weak##_##object;
#else
#define strongify(object) try{} @finally{} __typeof__(object) object = block##_##object;
#endif
#endif
#endif

    // 方案2 weak strong 可以解決延遲釋放  為加 weak strong 會(huì)延遲釋放
//    @weakify(self)
//    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(10 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
//        @strongify(self)
//        for(int i = 0;i< 10;i++){
//            [self requst:@"http://forspeed.onlinedown.net/down/YJPDFViewer2.0.zip"];
//        }
//    });

延遲釋放情形二)

這種定時(shí)器會(huì)引起延時(shí)釋放 , 如果非repeat 的話 直到 5 秒之后才會(huì)釋放 , 如果是repeat 的話 控制器 會(huì)一致導(dǎo)致控制器不釋放

    // 這種定時(shí)器會(huì)引起延時(shí)釋放 , 如果非repeat 的話 直到 5 秒之后才會(huì)釋放 , 如果是repeat 的話 控制器 會(huì)一致導(dǎo)致控制器不釋放
//    _timer = [NSTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(handleTimer:)
//                                                              userInfo:nil repeats:NO];

問題分析:
原因是 Timer 添加到 Runloop 的時(shí)候洒宝,會(huì)被 Runloop 強(qiáng)引用,然后 Timer 又會(huì)有一個(gè)對(duì) Target 的強(qiáng)引用(也就是 self )也就是說 NSTimer 強(qiáng)引用了 self 萌京,導(dǎo)致 self 一直不能被釋放掉雁歌,所以 self 的 dealloc 方法也一直未被執(zhí)行.

知道了錯(cuò)誤原因,就先查一下NSTimer的官方文檔看看具體用法細(xì)節(jié)知残,發(fā)現(xiàn)NSTimer還有一個(gè)規(guī)則:(在哪個(gè)線程創(chuàng)建就要在哪個(gè)線程停止靠瞎,否則會(huì)導(dǎo)致資源不能被正確的釋放。)那么問題來了:如果我就是想讓這個(gè) NSTimer 一直輸出,直到控制器 銷毀才停止并且釋放NSTimer乏盐。

問題關(guān)鍵:
問題的關(guān)鍵就在于 self 被 NSTimer 強(qiáng)引用了佳窑,如果能打破這個(gè)強(qiáng)引用,問題應(yīng)該就能決了父能。

解決方案:
使用weaktimer, HWWeakTimer , 不僅可以避免循環(huán)引用, 還可以解決延遲釋放


image.png

HWWeakTimer的源碼在本文中的demo中

    // 這里使用了weaktimer 之后 不管 是否repeat 控制器都可以及時(shí)的釋放 
    _timer = [HWWeakTimer scheduledTimerWithTimeInterval:5 target:self selector:@selector(handleTimer:)
                                                                  userInfo:nil repeats:YES];

延遲釋放情形三)

此時(shí)準(zhǔn)確的說控制器未被釋放 是控制器被循環(huán)引用了, 這是一種很常見的情形, 此處不贅述

不會(huì)導(dǎo)致延遲釋放但是會(huì)導(dǎo)致對(duì)象不釋放的情形

// 這里的不會(huì)引起延遲釋放 但是會(huì)導(dǎo)致 a 對(duì)象和b 對(duì)象不釋放 , 當(dāng)前的控制器對(duì)a b 對(duì)象無引用, 但是a b 對(duì)象內(nèi)部會(huì)形成引用環(huán)

  
    ClassA *a = [[ClassA alloc] init];
    ClassB *b = [[ClassB alloc] init];
    a.classb  = b;
    b.classa = a;
image.png

5) demo 的鏈接

AFN內(nèi)存泄漏demo

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末神凑,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子法竞,更是在濱河造成了極大的恐慌耙厚,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件岔霸,死亡現(xiàn)場(chǎng)離奇詭異薛躬,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)呆细,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門型宝,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人絮爷,你說我怎么就攤上這事趴酣。” “怎么了坑夯?”我有些...
    開封第一講書人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵岖寞,是天一觀的道長。 經(jīng)常有香客問我柜蜈,道長仗谆,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任淑履,我火速辦了婚禮隶垮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘秘噪。我一直安慰自己狸吞,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開白布指煎。 她就那樣靜靜地躺著蹋偏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪至壤。 梳的紋絲不亂的頭發(fā)上暖侨,一...
    開封第一講書人閱讀 51,292評(píng)論 1 301
  • 那天,我揣著相機(jī)與錄音崇渗,去河邊找鬼字逗。 笑死京郑,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的葫掉。 我是一名探鬼主播些举,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼俭厚!你這毒婦竟也來了户魏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤挪挤,失蹤者是張志新(化名)和其女友劉穎叼丑,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體扛门,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡鸠信,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了论寨。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片星立。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖葬凳,靈堂內(nèi)的尸體忽然破棺而出绰垂,到底是詐尸還是另有隱情,我是刑警寧澤火焰,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布劲装,位于F島的核電站,受9級(jí)特大地震影響昌简,放射性物質(zhì)發(fā)生泄漏酱畅。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一江场、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧窖逗,春花似錦址否、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至仗考,卻和暖如春音同,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背秃嗜。 一陣腳步聲響...
    開封第一講書人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來泰國打工权均, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留顿膨,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓叽赊,卻偏偏與公主長得像恋沃,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子必指,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354