NSTimer的使用
2018-12-06 本文已影响6人
点滴86
NSTimer的使用注意
一:循环引用问题
二:runloop的mode问题
三:在子线程中使用时需要启动runloop
使用方式一
#pragma mark - life cycle
- (void)dealloc
{
if (_myTimer) {
[self.myTimer invalidate];
self.myTimer = nil;
}
NSLog(@"DMFunctionNSTimerOneViewController 释放");
}
#pragma mark - private method
- (void)startTime
{
if (self.myTimer) {
[self.myTimer invalidate];
self.myTimer = nil;
}
self.totalTime = 60;
// 方式一
self.myTimer = [NSTimer timerWithTimeInterval:1 target:[DMWeakProxy proxyWithTarget:self] selector:@selector(myTimerAction) userInfo:nil repeats:YES];
// 需要将NSTimer以指定的mode加入到runloop中
[[NSRunLoop mainRunLoop] addTimer:self.myTimer forMode:NSRunLoopCommonModes];
}
- (void)myTimerAction
{
if (self.totalTime >= 0) {
NSLog(@"____%@", [NSNumber numberWithInteger:self.totalTime]);
self.timeShowLabel.text = [NSString stringWithFormat:@"%@s", [NSNumber numberWithInteger:self.totalTime]];
self.totalTime--;
}
}
注意:
NSTimer使用重复调用时会强引用target,为了避免循环引用加入了DMWeakProxy类
当使用timerWithTimeInterval这种方式创建NSTimer时,需要将NSTimer加入到runloop中
DMWeakProxy.h
#import <Foundation/Foundation.h>
NS_ASSUME_NONNULL_BEGIN
@interface DMWeakProxy : NSProxy
@property (nonatomic, weak, readonly) id target;
- (instancetype)initWithTarget:(id)target;
+ (instancetype)proxyWithTarget:(id)target;
@end
NS_ASSUME_NONNULL_END
DMWeakProxy.m
#import "DMWeakProxy.h"
@implementation DMWeakProxy
- (instancetype)initWithTarget:(id)target
{
_target = target;
return self;
}
+ (instancetype)proxyWithTarget:(id)target
{
return [[DMWeakProxy alloc] initWithTarget:target];
}
- (id)forwardingTargetForSelector:(SEL)selector
{
return _target;
}
- (void)forwardInvocation:(NSInvocation *)invocation
{
void *null = NULL;
[invocation setReturnValue:&null];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector
{
return [NSObject instanceMethodSignatureForSelector:@selector(init)];
}
- (BOOL)respondsToSelector:(SEL)aSelector
{
return [_target respondsToSelector:aSelector];
}
- (BOOL)isEqual:(id)object
{
return [_target isEqual:object];
}
- (NSUInteger)hash
{
return [_target hash];
}
- (Class)superclass
{
return [_target superclass];
}
- (Class)class
{
return [_target class];
}
- (BOOL)isKindOfClass:(Class)aClass
{
return [_target isKindOfClass:aClass];
}
- (BOOL)isMemberOfClass:(Class)aClass
{
return [_target isMemberOfClass:aClass];
}
- (BOOL)conformsToProtocol:(Protocol *)aProtocol
{
return [_target conformsToProtocol:aProtocol];
}
- (BOOL)isProxy
{
return YES;
}
- (NSString *)description
{
return [_target description];
}
- (NSString *)debugDescription
{
return [_target debugDescription];
}
@end

可以看到DMFunctionNSTimerOneViewController正常释放
方式二
#pragma mark - life cycle
- (void)dealloc
{
if (_myTimer) {
[self.myTimer invalidate];
self.myTimer = nil;
}
NSLog(@"DMFunctionNSTimerTwoViewController 释放");
}
#pragma mark - private method
- (void)startTime
{
if (self.myTimer) {
[self.myTimer invalidate];
self.myTimer = nil;
}
self.totalTime = 60;
// 方式二
// 这种方式启动的timer默认加到runloop的defalultModel中,会导致UIScrollView滑动过程中定时器失效
self.myTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:[DMWeakProxy proxyWithTarget:self] selector:@selector(myTimerAction) userInfo:nil repeats:YES];
}
- (void)myTimerAction
{
if (self.totalTime >= 0) {
NSLog(@"____%@", [NSNumber numberWithInteger:self.totalTime]);
self.timeShowLabel.text = [NSString stringWithFormat:@"%@s", [NSNumber numberWithInteger:self.totalTime]];
self.totalTime--;
}
}
注意
NSTimer 使用scheduledTimerWithTimeInterval方式时会默认加入到runloop的defalultModel中,会导致UIScrollView滑动过程中定时器失效

可以看到DMFunctionNSTimerTwoViewController正常释放
方式三:在子线程中使用NSTimer
#pragma mark - life cycle
- (void)dealloc
{
if (_myTimer) {
[self.myTimer invalidate];
self.myTimer = nil;
}
if (_myThread) {
[self stop];
}
NSLog(@"DMFunctionNSTimerThreeViewController 释放");
}
#pragma mark - private method
- (void)startTime
{
if (self.myTimer) {
[self.myTimer invalidate];
self.myTimer = nil;
}
self.myTimer = [NSTimer timerWithTimeInterval:1 target:[DMWeakProxy proxyWithTarget:self] selector:@selector(threadTimerRun) userInfo:nil repeats:YES];
__weak typeof(self) weakSelf = self;
self.myThread = [[DMThread alloc] initWithBlock:^{
[[NSRunLoop currentRunLoop] addTimer:weakSelf.myTimer forMode:NSRunLoopCommonModes];
// 这里需要注意不要使用[[NSRunLoop currentRunLoop] run]
// 因为通过run方法开启的runloop是无法停止的
while (weakSelf && !weakSelf.stopTimer) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
}];
[self.myThread start];
}
- (void)threadTimerRun
{
NSLog(@"thread - %@ - %s", [NSThread currentThread], __func__);
}
- (void)stop
{
[self performSelector:@selector(stopThread) onThread:self.myThread withObject:nil waitUntilDone:YES];
}
// 用于停止子线程的RunLoop
- (void)stopThread
{
// 设置标记为YES
self.stopTimer = YES;
// 停止RunLoop
CFRunLoopStop(CFRunLoopGetCurrent());
// 清空线程
self.myThread = nil;
}
注意
在子线程中使用NSTimer时,需要注意子线程默认没有开启runloop,需要手动开启runloop
DMThread.h
NS_ASSUME_NONNULL_BEGIN
@interface DMThread : NSThread
@end
NS_ASSUME_NONNULL_END
DMThread.m
#import "DMThread.h"
@implementation DMThread
- (void)dealloc
{
NSLog(@"DMThread 释放");
}
+ (void)exit
{
NSLog(@"DMThread exit");
[super exit];
}
@end

可以看到DMFunctionNSTimerThreeViewController以及DMThread线程正常释放