iOS內存優化之NSTimer存在的內存泄漏

  最近開始進行內存優化了,先從NSTimer的內存泄漏做起,NSTimer的造成內存泄漏的主要原因就是循環引用,NSTimer如果只響應一次的話是不存在循環引用的。主要是repeat爲YES時才存在循環引用。其中timer需要執行必須要加到runloop中才可以,schedule開頭的方法默認加入了NSdefaultModel了,但是加入runloop會對timer進行一個強持有。timer的target也會被timer強持有,造成target的dealloc不會被觸發
循環主要有兩種方式

1,定時器響應爲block時,需要配合@weakify(),@strongify()來消除循環引用
2, 定時器響應爲selector時。需要中介者來作爲timer的target,timer觸發的方法被轉發給真正的target這樣就OK了。

參考博客地址:NSTimer避坑指南
非常感謝作者詳細的分析,大家可以多多的關注作者,多多支持。
根據作者的文章,我這邊對通過中介者對NSTimer進行了優化,主要是優化有兩點就是NSTimer的響應爲selector時,不存在循環引用了,並且NSTimer會自動釋放,不需要開發者手動釋放,NSTimer響應爲block時,仍然用@weakify(),@strongify()來避免循環引用,但是NSTimer可以自動釋放,不用開發者自己手動去釋放了。
具體代碼如下:

import <Foundation/Foundation.h>

NS_ASSUME_NONNULL_BEGIN

@interface NSTimer (JKBasicProvider)
/**
 普通定時器,自動加入Default類型的runloop中
 */
+ (NSTimer *)jk_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                        target:(id)target
                                        repeat:(BOOL)repeat
                                         block:(void(^)(NSTimer *timer))block;

/**
 普通定時器,需要手動添加至runloop中
 */
+ (NSTimer *)jk_timerWithTimeInterval:(NSTimeInterval)interval
                               target:(id)target
                               repeat:(BOOL)repeat
                                block:(void(^)(NSTimer *timer))block;

/**
普通定時器,自動加入Default類型的runloop中
*/
+ (NSTimer *)jk_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                        target:(id)target
                                      selector:(SEL)selector
                                      userInfo:(nullable id)userInfo
                                       repeats:(BOOL)repeat;

/**
 普通定時器,需要手動添加至runloop中,可以進行暫停,重啓操作
 */
+ (NSTimer *)jk_timerWithTimeInterval:(NSTimeInterval)interval
                               target:(id)target
                             selector:(SEL)selector
                             userInfo:(nullable id)userInfo
                              repeats:(BOOL)repeat;
@end
#import "NSTimer+JKBasicProvider.h"

@interface JKTimerTarget : NSObject

@property (nonatomic,weak) id actionTarget;
@property (nonatomic,copy) void(^actionBlock)(NSTimer *timer);
@property (nonatomic, assign) SEL actionSelector;

@end

@implementation JKTimerTarget

- (instancetype)initWithSelector:(SEL)aSelector
                    actionTarget:(id)actionTarget
{
    self = [super init];
    if (self) {
        self.actionBlock = nil;
        self.actionTarget = actionTarget;
        self.actionSelector = aSelector;
    }
    return self;
}

- (instancetype)initWithBlock:(void(^)(NSTimer *timer))block
                 actionTarget:(id)actionTarget{
    self = [super init];
    if (self) {
        self.actionBlock = block;
        self.actionTarget = actionTarget;
    }
    return self;
}

- (void)timerAction:(NSTimer *)timer{
    if (self.actionTarget == nil) {
        [timer invalidate];
        timer = nil;
    }else{
        if (self.actionBlock) {
            self.actionBlock(timer);
        }else{
            IMP imp = [self.actionTarget methodForSelector:self.actionSelector];
            void (*func)(id, SEL,NSTimer *) = (void *)imp;
            func(self.actionTarget, self.actionSelector,timer);
        }
    }
}

@end


@implementation NSTimer (JKBasicProvider)

+ (NSTimer *)jk_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                        target:(id)target
                                        repeat:(BOOL)repeat
                                         block:(void(^)(NSTimer *timer))block
{
    JKTimerTarget *timerTarget = [[JKTimerTarget alloc] initWithBlock:block actionTarget:target];
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:interval target:timerTarget selector:@selector(timerAction:) userInfo:nil repeats:repeat];
    return timer;
}


+ (NSTimer *)jk_timerWithTimeInterval:(NSTimeInterval)interval
                               target:(id)target
                               repeat:(BOOL)repeat
                                block:(void(^)(NSTimer *timer))block
{
    JKTimerTarget *timerTarget = [[JKTimerTarget alloc] initWithBlock:block actionTarget:target];
    NSTimer *timer = [NSTimer timerWithTimeInterval:interval target:timerTarget selector:@selector(timerAction:) userInfo:nil repeats:repeat];
    return timer;
}

+ (NSTimer *)jk_scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                        target:(id)target
                                      selector:(SEL)selector
                                      userInfo:(nullable id)userInfo
                                       repeats:(BOOL)repeat
{
  JKTimerTarget *timerTarget = [[JKTimerTarget alloc] initWithSelector:selector actionTarget:target];
    NSTimer *timer = [NSTimer timerWithTimeInterval:interval target:timerTarget selector:@selector(timerAction:) userInfo:userInfo repeats:repeat];
    return timer;
}


+ (NSTimer *)jk_timerWithTimeInterval:(NSTimeInterval)interval
                               target:(id)target
                             selector:(SEL)selector
                             userInfo:(nullable id)userInfo
                              repeats:(BOOL)repeat
{
    JKTimerTarget *timerTarget = [[JKTimerTarget alloc] initWithSelector:selector actionTarget:target];
    NSTimer *timer = [NSTimer timerWithTimeInterval:interval target:timerTarget selector:@selector(timerAction:) userInfo:userInfo repeats:repeat];
    return timer;
}

由於內容較少,就沒有單獨給大家封裝成庫,大家可以直接複製粘貼使用。
更多技術乾貨文章可以掃描下方二維碼:
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章