NSTimer如何使用 
方法1: Creates a timer and schedules it on the current run loop in the default mode.
 
1 2 3 4 5 + (NSTimer  *)scheduledTimerWithTimeInterval:(NSTimeInterval )ti                                       target:(id )aTarget                                     selector:(SEL)aSelector                                     userInfo:(id )userInfo                                      repeats:(BOOL )yesOrNo; 
方法2: Initializes a timer object with the specified object and selector.
 
You must add the new timer to a run loop, using addTimer:forMode: . Then, after ti seconds have elapsed, the timer fires, sending the message aSelector to target. (If the timer is configured to repeat, there is no need to subsequently re-add the timer to the run loop.)
 
1 2 3 4 5 6 7 + (NSTimer  *)timerWithTimeInterval:(NSTimeInterval )ti                              target:(id )aTarget                            selector:(SEL)aSelector                            userInfo:(id )userInfo                             repeats:(BOOL )yesOrNo; [[NSRunLoop  mainRunLoop] addTimer:self .timer forMode:NSDefaultRunLoopMode ]; 
这两个方法是等价的,区别是第一个方法默认创建了一个NSTimer并自动添加到了当前线程的Runloop中去,第二个需要我们手动添加。如果当前线程是主线程的话,某些UI事件,比如UIScrollView的拖拽操作,会将Runloop切换成UITrackingRunLoopMode,这时候,默认的NSDefaultRunLoopMode模式中注册的事件是不会被执行的。所以为了设置一个不会被UI干扰的Timer,我们需要手动将timer的当前RunloopMode设置为NSRunLoopCommonModes,这个模式等效于NSDefaultRunLoopMode和UITrackingRunLoopMode的结合。
由于NSTimer会强引用Target,RunLoop会强引用NSTimer,若ViewController再强引用NSTimer,NSTimer找不到合适的时机释放的时候,很容易造成内存泄漏,这个问题如何解决?
如何解决NSTimer找不到释放时机的问题? 方案1,使用官方API带Block的方式来创建NSTimer 具体的创建方法如下,但是仅支持iOS 10以上的系统。
1 + (NSTimer  *)timerWithTimeInterval:(NSTimeInterval )interval repeats:(BOOL )repeats block:(void  (^)(NSTimer  *timer))block API_AVAILABLE(macosx(10.12 ), ios(10.0 ), watchos(3.0 ), tvos(10.0 )); 
在ViewController中的使用方法:
1 2 3 4 5 6 __weak  typeof (self )weakSelf = self ; self .timer = [NSTimer  timerWithTimeInterval:1.0  repeats:YES  block:^(NSTimer  * _Nonnull timer) {    NSLog (@"第%ld次执行" , (long )weakSelf.count);     weakSelf.count ++; }]; [[NSRunLoop  currentRunLoop] addTimer:self .timer forMode:NSRunLoopCommonModes ]; 
方案2,使用Block的方式来创建NSTimer 具体实现方式是,需要创建一个NSTimer的分类,让外部传入一个Block,NSTimer每次到时间就执行传入进来的Block。新建一个分类:
1 2 3 4 5 6 7 8 9 10 11 12 13 #import <Foundation/Foundation.h>  NS_ASSUME_NONNULL_BEGIN @interface  NSTimer  (BlockSupport )+ (NSTimer  *)cd_timerWithTimeInterval:(NSTimeInterval )interval                                        repeats:(BOOL )repeats                                          block:(void (^)(void ))block; @end NS_ASSUME_NONNULL_END 
NSTimer+BlockSupport.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #import "NSTimer+BlockSupport.h"  @implementation  NSTimer  (BlockSupport )+ (NSTimer  *)cd_timerWithTimeInterval:(NSTimeInterval )interval                               repeats:(BOOL )repeats                                 block:(void (^)(void ))block {     return  [self  scheduledTimerWithTimeInterval:interval                                          target:self                                         selector:@selector (cd_blockInvoke:)                                        userInfo:[block copy ]                                         repeats:repeats]; } + (void )cd_blockInvoke:(NSTimer  *)timer {     void  (^block)(void ) = timer.userInfo;     if (block) {         block();     } } @end 
在ViewController中使用方法:
1 2 3 4 5 6 __weak  typeof (self )weakSelf = self ; self .timer = [NSTimer  cd_timerWithTimeInterval:1.0  repeats:YES  block:^{    NSLog (@"第%ld次执行" , (long )weakSelf.count);     weakSelf.count ++; }]; [[NSRunLoop  currentRunLoop] addTimer:self .timer forMode:NSRunLoopCommonModes ]; 
方案3,自定义NSTimer的Target 封装自定义NSTimer的Target和NSTimer的工具类,让NSTimer在合适的时机释放。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #import <Foundation/Foundation.h>  NS_ASSUME_NONNULL_BEGIN @interface  CDTimerTarget  : NSObject @property  (nonatomic ,weak ) id  target;@property  (nonatomic ,assign ) SEL selector;@end NS_ASSUME_NONNULL_END 
CDTimerTarget.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 #import "CDTimerTarget.h"  @implementation  CDTimerTarget - (void )fire:(NSTimer  *)timer {          if  (self .target) { #pragma  clang diagnostic push #pragma  clang diagnostic ignored "-Warc-performSelector-leaks"          [self .target performSelector:self .selector withObject:timer.userInfo]; #pragma  clang diagnostic pop     }else  {         [timer invalidate];     } } @end 
CDTimer.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #import <Foundation/Foundation.h>  NS_ASSUME_NONNULL_BEGIN @interface  CDTimer  : NSObject + (NSTimer  *)scheduleTimerWithTimerInterval:(NSTimeInterval )interval                                      target:(id )aTarget                                    selector:(SEL)aSelector                                    userInfo:(id )userInfo                                     repeats:(BOOL )repeats; @end NS_ASSUME_NONNULL_END 
CDTimer.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #import "CDTimer.h"  #import "CDTimerTarget.h"  @implementation  CDTimer + (NSTimer  *)scheduleTimerWithTimerInterval:(NSTimeInterval )interval                                      target:(id )aTarget                                    selector:(SEL)aSelector                                    userInfo:(id )userInfo                                     repeats:(BOOL )repeats {          CDTimerTarget *timerTarget = [[CDTimerTarget alloc]init];     timerTarget.target = aTarget;     timerTarget.selector = aSelector;     NSTimer  *timer = [NSTimer  scheduledTimerWithTimeInterval:interval                                                       target:timerTarget                                                     selector:@selector (fire:)                                                     userInfo:userInfo                                                      repeats:repeats];     return  timer; } @end 
在ViewController中的用法:
1 self .timer = [CDTimer scheduleTimerWithTimerInterval:1.0  target:self  selector:@selector (repeatAction) userInfo:nil  repeats:YES ];
方案4,自定义NSProxy子类,作为NSTimer的Target TargetProxy.h
1 2 3 4 5 6 7 8 9 10 11 #import <Foundation/Foundation.h>  NS_ASSUME_NONNULL_BEGIN @interface  TargetProxy  : NSProxy + (instancetype )proxyWithTarget:(id )target; @end NS_ASSUME_NONNULL_END 
TargetProxy.m
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 #import "TargetProxy.h"  @interface  TargetProxy ()@property  (nonatomic , weak )id  target;@end @implementation  TargetProxy + (instancetype )proxyWithTarget:(id )target {     TargetProxy *proxy = [[self  class ] alloc];     proxy.target = target;     return  proxy; } - (NSMethodSignature  *)methodSignatureForSelector:(SEL)sel {     return  [self .target methodSignatureForSelector:sel]; } - (void )forwardInvocation:(NSInvocation  *)invocation {          if  ([self .target respondsToSelector:invocation.selector]) {                  [invocation invokeWithTarget:self .target];     }else  {                  [invocation doesNotRecognizeSelector:invocation.selector];     } } @end 
在ViewController中的使用方法:
1 self .timer = [NSTimer  scheduledTimerWithTimeInterval:1.0  target:[TargetProxy proxyWithTarget:self ] selector:@selector (repeatAction) userInfo:nil  repeats:YES ];
参考链接 NSTimer 
NSProxy解决NSTimer和CADisplayLink的循环引用 
《Effective Objective-C 2.0 编写高质量iOS与OS X代码的52个有效方法》