在iOS 10系統以前,系統的NSTimer是會引發循環引用的,致使內存泄漏。下面就針對這個問題給出幾種解決方法。ios
在iOS 10之後系統,蘋果針對NSTimer進行了優化,使用Block回調方式,解決了循環引用問題。git
//API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
[NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
// Do some things
}];
複製代碼
這個iOS 10方法能解決循環引用問題。可是咱們目前大部分仍是要適配iOS10如下系統。github
平時咱們都是這麼使用NSTimermacos
[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(testTimer) userInfo:nil repeats:YES]; //或者 self.myTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(testTimer) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:self.myTimer forMode:NSDefaultRunLoopMode]; - (void)testTimer { NSLog(@"Do Some Things"); } //中止Timer - (void)dealloc { [self.myTimer invalidate]; self.myTimer = nil; } 複製代碼
使用(void)didMoveToParentViewController:(UIViewController *)parent
方法,在這個方法裏清掉定時器,就會釋放Timer對象,也就解決了強引用。即調用dealloc方法了。bash
注意:針對PresentVC 不適用。markdown
//生命週期 移除childVC的時候 - (void)didMoveToParentViewController:(UIViewController *)parent { if (parent == nil) { [self.myTimer invalidate]; self.myTimer = nil; } } 複製代碼
消息傳遞沒有什麼是中間件不能解決的,若是有,那就在加中間件 O(∩_∩)O哈哈~oop
//定義箇中間件屬性 @property (nonatomic, strong) id target; _target = [NSObject new]; class_addMethod([_target class], @selector(testTimer), (IMP)timerIMP, "v@:"); //這裏換成_target 不用self了。 這就沒有了循環引用了 self.myTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:_target selector:@selector(testTimer) userInfo:nil repeats:YES]; [[NSRunLoop currentRunLoop] addTimer:self.myTimer forMode:NSDefaultRunLoopMode]; void timerIMP(id self, SEL _cmd) { NSLog(@"Do some things"); } //中止Timer - (void)dealloc { [self.myTimer invalidate]; self.myTimer = nil; NSLog(@"Timer dealloc"); } 複製代碼
此時也是能夠的。優化
新建一個類TimerProxy,繼承NSProxy。atom
設置一個屬性spa
//注意這裏要使用weak
@property (nonatomic, weak) id target;
複製代碼
而後在實現兩個方法:
/** 方法簽名 */ - (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { return [self.target methodSignatureForSelector:sel]; } /** 消息轉發 */ - (void)forwardInvocation:(NSInvocation *)invocation { [invocation invokeWithTarget:self.target]; } 在你須要的地方,而後導入TimerProxy頭文件使用 @property (nonatomic, strong) TimerProxy *timerProxy; _timerProxy = [TimerProxy alloc];//注意這裏只有alloc方法 _timerProxy.target = self; self.myTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:_timerProxy selector:@selector(testTimer) userInfo:nil repeats:YES]; 複製代碼
這時也能夠解決循環引用問題。
新建一個NSTimer分類, QLTimer. 定義一個加方法
/** 定義一個加方法 */ + (NSTimer *)QLscheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval repeats:(BOOL)repeats block:(void(^)(void))timerBlock; //實現方法 +(NSTimer *)QLscheduledTimerWithTimeInterval:(NSTimeInterval)timeInterval repeats:(BOOL)repeats block:(nonnull void (^)(void))timerBlock { return [self scheduledTimerWithTimeInterval:timeInterval target:self selector:@selector(QLTimerHandle:) userInfo:[timerBlock copy] //注意copy repeats:repeats]; } +(void)QLTimerHandle:(NSTimer *)timer { void(^block)(void) = timer.userInfo; if (block) { block(); } } 使用 __block typeof(self) weakSelf = self; self.myTimer = [NSTimer QLscheduledTimerWithTimeInterval:1.0 repeats:YES block:^{ __block typeof(weakSelf) strongSelf = weakSelf; [strongSelf testTimer]; }]; 複製代碼
聽說是通過蘋果官方確認過的方法:used with GCD queues.