iOS 定時(shí)器NSTimer自動(dòng)釋放

NSTimer是我們平時(shí)在項(xiàng)目中使用比較多的,但是使用的時(shí)候需要比較注意,需要在目標(biāo)對(duì)象釋放之前就要結(jié)束定時(shí)器,不然會(huì)造成內(nèi)存泄露。
具體原因就是NSTimer的常用的方法:

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;

這兩個(gè)方法需要設(shè)置target,但是系統(tǒng)內(nèi)部會(huì)對(duì)target強(qiáng)引用,導(dǎo)致target無(wú)法釋放。
有的時(shí)候我們希望定時(shí)器在目標(biāo)對(duì)象釋放的時(shí)候能夠自己結(jié)束,但是我們又無(wú)法直接修改系統(tǒng)內(nèi)部的api。
這個(gè)時(shí)候我們可以利用利用一種比較簡(jiǎn)單地思路來(lái)實(shí)現(xiàn):引入一個(gè)中間對(duì)象TimerDelegate,


image.png

將定時(shí)器的目標(biāo)對(duì)象設(shè)為TimerDelegate,再由TimerDelegate去調(diào)用原target的方法,這樣每次調(diào)用之前我們都可以知道target是否已經(jīng)被釋放了。

實(shí)現(xiàn):

新建對(duì)象TimerDelegate

//  TimerDelegate.h

#import <Foundation/Foundation.h>

@interface TimerDelegate : NSObject

- (void)handleTarget:(id)aTarget  selector:(SEL)aSel  userInfo:(id)userInfo;


@property (weak,nonatomic)id desTarget;   //注意弱引用
@property (assign,nonatomic)SEL selector;
@property (strong,nonatomic)id userInfo;

用runtime交換這兩個(gè)方法

//  NSTimer+Release.m
+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        NSArray *selNameArray = @[@"scheduledTimerWithTimeInterval:target:selector:userInfo:repeats:",@"timerWithTimeInterval:target:selector:userInfo:repeats:"];
        for (NSString *name in selNameArray) {
            SEL oriSel = NSSelectorFromString(name);
            NSString *newSelString = [NSString stringWithFormat:@"YT_%@",NSStringFromSelector(oriSel)];
            SEL newSel = NSSelectorFromString(newSelString);
            Class class = object_getClass((id)self);
            [RuntimeManager swizzleClass:class OrigionMethod:oriSel withNewMethod:newSel];
        }
    });
}

如果定時(shí)器不重復(fù)則不需要處理

+ (NSTimer *)YT_scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo{
    if(yesOrNo) {
    TimerDelegate *timerDelegate = [[TimerDelegate alloc]init];
    //將原目標(biāo)對(duì)象和參數(shù)傳遞給delegate
    [timerDelegate handleTarget:aTarget selector:aSelector userInfo:userInfo];
    SEL sel = NSSelectorFromString(@"fireTimer:");
    
    //調(diào)用初始的方法,此處將定時(shí)器對(duì)象設(shè)置TimerDelegate, 然后調(diào)用TimerDelegate對(duì)象的fireTimer方法
    NSTimer* timer = [NSTimer YT_scheduledTimerWithTimeInterval:ti target:timerDelegate selector:sel userInfo:userInfo repeats:yesOrNo];
    return timer;
 
   }
    else {
    return  [self YT_scheduledTimerWithTimeInterval:ti target:aTarget selector:aSelector userInfo:userInfo repeats:yesOrNo];
    }
}

然后實(shí)現(xiàn)NStimerDelegate.m文件里的方法:

// NSTimerDelegate.m

- (void) fireTimer:(NSTimer *)timer {
    if(!_desTarget) {   //判斷原對(duì)象是否已經(jīng)釋放
        [timer invalidate];
        timer = nil;
        return ;
    }
    //執(zhí)行原目標(biāo)對(duì)象的方法
  if ([_desTarget respondsToSelector:self.selector]) {
        #pragma clang diagnostic push
        #pragma clang diagnostic ignored "-Warc-performSelector-leaks"
       
        [self.desTarget performSelector:self.selector withObject:timer];

        #pragma clang diagnostic pop
    }
}


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

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