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