iOS中Cell或SubView的timer釋放問題

問題

在很多場景下可能需要在cell或view中添加計時器進行倒計時的處理,但是這里會有一個問題就是timer的釋放問題允坚。如果沒有在controller釋放時進行釋放像寒,timer會一直被強引用造成內(nèi)存泄露。

在網(wǎng)上看到了幾種解決方案,但是都不是很理想汁蝶,最終在這篇文章中找到了覺得是最好的方法。但是在這個文章里用到了RAC的開源庫论悴,對于大部分人來說可能并不會引用這個庫掖棉。在這里會通過類別的方式來對最后一種方案來進行改造,使得可以在任何地方使用膀估。

方案分析

在這里簡單說明下解決方案幔亥。由于在cell中使用了NSTimer作為屬性,在這種情況下cell不會自動調(diào)用-dealloc方法來釋放內(nèi)存察纯。這樣的話我們只能通過在controller釋放內(nèi)存時來釋放timer來達到目的帕棉。在這里我們會添加兩個類別针肥,一個是UIView+ManagerCaller.h類別來獲取持有view的controller實例,一個是NSObject+DeallocBlock.h類別來添加調(diào)用-dealloc函數(shù)時的回調(diào)函數(shù)香伴。

貼下代碼

#import "UIView+ManagerCaller.h"

@implementation UIView (ManagerCaller)

- (UIViewController*)getManagerController
{
    UIResponder* responder = self;
    while (responder && ![responder isKindOfClass:[UIViewController class]]) {
        responder = responder.nextResponder;
    }
    if (!responder) {
        return nil;
    }
    if ([responder isKindOfClass:[UIViewController class]]) {
        return (UIViewController*)responder;
    }
    return nil;
}
@end
#import "NSObject+DeallocBlock.h"
#import <objc/runtime.h>

@interface FDOnDeallocBlockHolder : NSObject

@property (copy, nonatomic) FDObjectDeallocBlock block;
@property (assign, nonatomic) void* key;

@end

@implementation FDOnDeallocBlockHolder

- (instancetype)initWithBlock: (FDObjectDeallocBlock)block
{
    self = [super init];
    self.block = block;
    return self;
}

- (void)dealloc
{
    FDObjectDeallocBlock block = _block;
    _block = nil;
    if (block) {
        block();
    }
}

@end

@implementation NSObject (DeallocBlock)

- (void*)addOnDeallocBlock: (FDObjectDeallocBlock)block forKey: (void*)key
{
    FDOnDeallocBlockHolder* holder = [[FDOnDeallocBlockHolder alloc] initWithBlock:block];
    
    if (!key) {
        key = ((__bridge void*)holder);
    } else {
        holder.key = key;
    }
    
    objc_setAssociatedObject(self, key, holder, OBJC_ASSOCIATION_RETAIN);
    [self cacheOnDeallocBlockHolder:holder];
    return key;
}

- (void*)addOnDeallocBlock: (FDObjectDeallocBlock)block
{
    return [self addOnDeallocBlock:block forKey:nil];
}

- (BOOL)hasOnDeallocBlockWithKey: (void*)key
{
    return objc_getAssociatedObject(self, key) != nil;
}

- (void)removeOnDeallocBlockByKey: (void*)key
{
    NSMutableArray* list = [self onDeallocBlockHolderList];
    for (NSValue* holderReference in list) {
        FDOnDeallocBlockHolder* holder = holderReference.nonretainedObjectValue;
        if (holder && (holder == key || holder.key == key)) {
            holder.block = nil;
            [list removeObject:holderReference];
            break;
        }
    }
    objc_setAssociatedObject(self, key, nil, OBJC_ASSOCIATION_RETAIN);
}

- (void)cacheOnDeallocBlockHolder: (FDOnDeallocBlockHolder*)holder
{
    NSMutableArray* list = [self onDeallocBlockHolderList];
    [list addObject:[NSValue valueWithNonretainedObject:holder]];
}

- (NSMutableArray<NSValue*>*)onDeallocBlockHolderList
{
    NSMutableArray* list = objc_getAssociatedObject(self, @selector(onDeallocBlockHolderList));
    if (!list) {
        list = [[NSMutableArray alloc] init];
        objc_setAssociatedObject(self, @selector(onDeallocBlockHolderList), list, OBJC_ASSOCIATION_RETAIN);
    }
    return list;
}

- (void)executeAllOnDeallocBlocks
{
    NSMutableArray* list = [self onDeallocBlockHolderList];
    for (NSValue* holderReference in list) {
        FDOnDeallocBlockHolder* holder = holderReference.nonretainedObjectValue;
        if (!holder) {
            continue;
        }
        FDObjectDeallocBlock block = holder.block;
        holder.block = nil;
        if (!block) {
            continue;
        }
        void* key = ((__bridge void*)holder);
        objc_setAssociatedObject(self, key, nil, OBJC_ASSOCIATION_RETAIN);
        block();
    }
    [list removeAllObjects];
}
@end

代碼分析

  • UIView+ManagerCaller.h
    這個類別的邏輯就是通過遍歷View的所有Responder來找到持有View的controller慰枕。

  • NSObject+DeallocBlock.h
    在OC中釋放內(nèi)存邏輯是先釋放實例自身再釋放實例成員變量。在這里我們通過這個特性來給實例添加一個新成員變量來實現(xiàn)回調(diào)即纲,把回調(diào)函數(shù)賦值給新成員變量具帮,在實例自身釋放后新成員變量釋放時調(diào)用-dealloc方法時執(zhí)行回調(diào)來達到目的。

  • 添加回調(diào)時機
    在這里會有一個添加回調(diào)時機的問題低斋,因為由于加載cell方式不同會略有不同蜂厅。如果通過代碼加載cell的話,在執(zhí)行-didMoveToSuperview函數(shù)時就可以獲取到controller實例膊畴。但是如果通過XIB加載cell的情況下葛峻,在調(diào)用這個函數(shù)時是獲取不到controller實例的。為了保險起見巴比,可以統(tǒng)一放到-didMoveToWindow函數(shù)里來添加回調(diào)函數(shù)。這里也要注意需要做好判斷邏輯不要重復(fù)添加回調(diào)函數(shù)礁遵,因為-didMoveToWindow函數(shù)可能會在不同情況下觸發(fā)多次轻绞。

效果演示

下面貼兩張圖,分別是添加回調(diào)函數(shù)前后Log對比佣耐。


添加回調(diào)函數(shù)前
添加回調(diào)函數(shù)后

總結(jié)

在上面這個問題里政勃,主要解決思路就是怎么讓cell或者subview在controller觸發(fā)-dealloc后執(zhí)行回調(diào)函數(shù),這里涉及到了內(nèi)存釋放機制兼砖,我在這里也只是簡單應(yīng)用了而已奸远,可能還有一些情況沒有考慮到會有其他問題。后續(xù)還是要再研究下內(nèi)存釋放機制的更深層的內(nèi)容讽挟。

Demo地址

參考文章
如何釋放含有NSTimer的UITableViewCell(SubView)

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末懒叛,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子耽梅,更是在濱河造成了極大的恐慌薛窥,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件眼姐,死亡現(xiàn)場離奇詭異诅迷,居然都是意外死亡,警方通過查閱死者的電腦和手機众旗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門罢杉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人贡歧,你說我怎么就攤上這事滩租「承悖” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵持际,是天一觀的道長沃琅。 經(jīng)常有香客問我,道長蜘欲,這世上最難降的妖魔是什么益眉? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮姥份,結(jié)果婚禮上郭脂,老公的妹妹穿的比我還像新娘。我一直安慰自己澈歉,他們只是感情好展鸡,可當(dāng)我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著埃难,像睡著了一般莹弊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上涡尘,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天忍弛,我揣著相機與錄音,去河邊找鬼考抄。 笑死细疚,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的川梅。 我是一名探鬼主播疯兼,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼贫途!你這毒婦竟也來了吧彪?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤潮饱,失蹤者是張志新(化名)和其女友劉穎来氧,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體香拉,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡啦扬,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了凫碌。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片扑毡。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖盛险,靈堂內(nèi)的尸體忽然破棺而出瞄摊,到底是詐尸還是另有隱情勋又,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布换帜,位于F島的核電站楔壤,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏惯驼。R本人自食惡果不足惜蹲嚣,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望祟牲。 院中可真熱鬧隙畜,春花似錦、人聲如沸说贝。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乡恕。三九已至言询,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間傲宜,已是汗流浹背倍试。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蛋哭,地道東北人。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓涮母,卻偏偏與公主長得像谆趾,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子叛本,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 42,722評論 2 345

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

  • 37.cocoa內(nèi)存管理規(guī)則 1)當(dāng)你使用new沪蓬,alloc或copy方法創(chuàng)建一個對象時,該對象的保留計數(shù)器值為1...
    如風(fēng)家的秘密閱讀 829評論 0 4
  • 面試題參考1 : 面試題[http://www.cocoachina.com/ios/20150803/12872...
    江河_ios閱讀 1,706評論 0 4
  • 文/Jack_lin(簡書作者)原文鏈接:http://www.reibang.com/p/5d2163640e2...
    筆筆請求閱讀 539評論 0 0
  • *面試心聲:其實這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,124評論 29 470
  • 咖啡,慢慢啜之,香醇飄逸,神清氣爽,只覺四肢通體舒暢,苦里帶著香,香里帶著甜,真正的去感受一杯咖啡,感受的是一種優(yōu)...
    極樂一切閱讀 964評論 2 6