利用RunTime解決由NSTimer導(dǎo)致的內(nèi)存泄漏

NSTimer使用場景

用NSTimer來實(shí)現(xiàn)每隔一定時間執(zhí)行制定的任務(wù),例如最常見的廣告輪播圖,使用NSTimer實(shí)現(xiàn)這個功能很簡單代碼如下

NSTimer *_timer;? ? _timer = [NSTimer timerWithTimeInterval:1target:selfselector:@selector(timerEvent) userInfo:nilrepeats:YES];? ? [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];


但是要記住只要觸發(fā)了計(jì)時器這種操作在不用時一定要把計(jì)時器終止掉

[_timer invalidate];

一般我們終止這個操作都需要在這個界面銷毀時。但是我們在初始化NSTimer時指定了觸發(fā)事件為self,所以說self被NSTimer強(qiáng)引用了,而NSTimer對象又被加入了當(dāng)前的循環(huán)中,所以說NSTimer被 Runloop 強(qiáng)引用了,所以導(dǎo)致self不會被釋放掉就不會觸發(fā)dealloc方法

實(shí)際上想這樣操作

-(void)dealloc{? ? [_timer invalidate];}

但是由于self對象被持有,所有不會走dealloc,導(dǎo)致雖然已經(jīng)退出當(dāng)前界面了,但是計(jì)時器還是一致再執(zhí)行,出現(xiàn)內(nèi)存泄漏。

解決方法

思路很簡單,初始化NSTimer時把觸發(fā)事件的target替換成一個單獨(dú)的對象,然后這個對象中NSTimer的SEL方法觸發(fā)時讓這個方法在當(dāng)前的視圖self中實(shí)現(xiàn)。

利用RunTime在target對象中動態(tài)的創(chuàng)建SEL方法,然后target對象關(guān)聯(lián)當(dāng)前的視圖self,當(dāng)target對象執(zhí)行SEL方法時,取出關(guān)聯(lián)對象self,然后讓self執(zhí)行該方法。

實(shí)現(xiàn)代碼

@interfaceTableViewController()@property(nonatomic,strong)idtimerTarget;@endstaticconstvoid* TimerKey = @"TimerKey";staticconstvoid* weakKey = @"weakKey";@implementationTableViewController- (void)viewDidLoad {? ? [superviewDidLoad];? ? _timerTarget = [NSObjectnew];//初始化timerTarge對象class_addMethod([_timerTarget class],@selector(timerEvent), (IMP)timMethod,"v@:");//動態(tài)創(chuàng)建timerEvent方法NSTimer *_timer;? ? _timer = [NSTimer timerWithTimeInterval:1target:_timerTarget selector:@selector(timerEvent) userInfo:nilrepeats:YES];? ? [[NSRunLoop currentRunLoop] addTimer:_timer forMode:NSRunLoopCommonModes];//創(chuàng)建計(jì)時器target對象為_timerTargetobjc_setAssociatedObject(_timerTarget, TimerKey, _timer, OBJC_ASSOCIATION_RETAIN_NONATOMIC);? ? objc_setAssociatedObject(_timerTarget, weakKey,self, OBJC_ASSOCIATION_ASSIGN);//將self對象與NSTimer對象與_timerTarget對象關(guān)聯(lián)}voidtimMethod(idself,SEL _cmd){? ? TableViewController *tabview = objc_getAssociatedObject(self, weakKey);? ? [tabview performSelector:_cmd];}-(void)timerEvent{NSLog(@"%@",NSStringFromClass([selfclass]));}-(void)dealloc{? ? NSTimer *timer = objc_getAssociatedObject(_timerTarget, TimerKey);? ? [timer invalidate];NSLog(@"%@--dealloc",NSStringFromClass([selfclass]));}


這樣當(dāng)視圖銷毀時因?yàn)楫?dāng)前視圖不被任何對象所持有,所以會走dealloc方法,然后NSTimer執(zhí)行invalidate也被銷毀釋放掉了。

說明

objc_setAssociatedObject(_timerTarget, weakKey, self,OBJC_ASSOCIATION_ASSIGN);

1

在把_timerTarget與self關(guān)聯(lián)時關(guān)聯(lián)的屬性一定要設(shè)置為OBJC_ASSOCIATION_ASSIGN。OBJC_ASSOCIATION_ASSIGN為弱指針類型,如果設(shè)置為強(qiáng)制針,那么self與_timerTarget就會發(fā)生相互強(qiáng)引用但是內(nèi)存不能正確釋放。

關(guān)于使用到的Runtime

class_addMethod([_timerTarget class],@selector(timerEvent),(IMP)timMethod,"v@:");

1

動態(tài)的為類添加一個timerEvent的Objective-C方法,這個方法是由C的timMethod方法來實(shí)現(xiàn)的

voidtimMethod(id self,SEL _cmd){? ? TableViewController *tabview = objc_getAssociatedObject(self, weakKey);? ? [tabview performSelector:_cmd];}

該方法是取到_timerTarget關(guān)聯(lián)的對象,然后讓該對象去執(zhí)行timerEvent方法。

"v@:"是方法的參數(shù),關(guān)于參數(shù)解釋參考Objective-C type encodings

objc_setAssociatedObject(id object,constvoid*key, id value, objc_AssociationPolicy policy)objc_getAssociatedObject(id object,constvoid*key)

這組方法是設(shè)置關(guān)聯(lián)對象與獲取關(guān)聯(lián)對象key是關(guān)聯(lián)對象的key。

轉(zhuǎn)載至:http://blog.csdn.net/ggghub/article/details/50240225

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 轉(zhuǎn)至元數(shù)據(jù)結(jié)尾創(chuàng)建: 董瀟偉,最新修改于: 十二月 23, 2016 轉(zhuǎn)至元數(shù)據(jù)起始第一章:isa和Class一....
    40c0490e5268閱讀 2,030評論 0 9
  • runtime 和 runloop 作為一個程序員進(jìn)階是必須的,也是非常重要的, 在面試過程中是經(jīng)常會被問到的, ...
    made_China閱讀 1,269評論 0 7
  • 不是路癡的人真的不懂我們生存有多艱難。 1 同事、朋友,甚至親人中,女生患有“路癡”的不在少數(shù)?。〕3J菛|南西北分...
    曼季風(fēng)閱讀 1,093評論 0 1
  • document.documentElement.scrollTop document.body.scrollTo...
    有個水友閱讀 252評論 0 0

友情鏈接更多精彩內(nèi)容