iOS开发中,涉及到定时的问题,我们通常使用NSTimer来解决,例如下面的代码。
SFClass.h
#import <Foundation/Foundation.h> @interface SFClass : NSObject - (void)startPolling; - (void)stopPolling; @end
SFClass.m
#import "SFClass.h" @implementation SFClass { NSTimer *_pollTimer; } - (void)dealloc { [_pollTimer invalidate]; } - (void)stopPolling { [_pollTimer invalidate]; _pollTimer = nil; } - (void)doSomething { } - (void)startPolling { _pollTimer = [NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:@selector(doSomething) userInfo:nil repeats:YES]; } @end
由于定时器会保留target对象,所以上面的代码会造成SFClass对象和NSTimer对象的相互引用,会导致内存泄漏问题。
我们可以通过下面的方式来解决。
NSTimer+SFBlocksSupport.h
#import <Foundation/Foundation.h> @interface NSTimer (SFBlocksSupport) + (NSTimer *)sf_scheduleTimerWithTimeInterval:(NSTimeInterval)interval block:(void (^)(void))block repeats:(BOOL)repeats; @end
NSTimer+SFBlocksSupport.m
#import "NSTimer+SFBlocksSupport.h" @implementation NSTimer (SFBlocksSupport) + (NSTimer *)sf_scheduleTimerWithTimeInterval:(NSTimeInterval)interval block:(void (^)(void))block repeats:(BOOL)repeats { return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(sf_blockInvoke:) userInfo:[block copy] repeats:repeats]; } + (void)sf_blockInvoke:(NSTimer *)timer { void (^block)(void) = timer.userInfo; if (block) { block(); } } @end
然后,我们使用定时器的代码就要修改为下面的样子
- (void)startPolling { _pollTimer = [NSTimer sf_scheduleTimerWithTimeInterval:1.0 block:^{ [self doSomething]; } repeats:YES]; }
仔细看看代码,发现依然有保留环,再修改一下:
- (void)startPolling { __weak typeof(self) weakSelf = self; _pollTimer = [NSTimer sf_scheduleTimerWithTimeInterval:1.0 block:^{ SFClass *strongSelf = weakSelf; [strongSelf doSomething]; } repeats:YES]; }
最终解决了相互引用的问题。