最近開始進行內存優化了,先從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;
}
由於內容較少,就沒有單獨給大家封裝成庫,大家可以直接複製粘貼使用。
更多技術乾貨文章可以掃描下方二維碼: