NSTimer的创建方式:
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti
invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti
invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;
+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)ti ‘
target:(id)aTarget selector:(SEL)aSelector
userInfo:(nullable id)userInfo
repeats:(BOOL)yesOrNo;
需要手动添加到runloop
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti
target:(id)aTarget selector:(SEL)aSelector
userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo;
//系统默认把timer添加到当前runloop的NSDefaultRunLoopMode中
+ (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));
//需要手动添加到runloop
/// Creates and returns a new NSTimer object initialized with the
specified block object and schedules it on the current run
loop in the default mode. - parameter: block The execution body
of the timer; the timer itself is passed as the parameter to
this block when executed to aid in avoiding cyclical references
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval
repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block
API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
//系统默认把timer添加到当前runloop的NSDefaultRunLoopMode中
/// Initializes a new NSTimer object using the block as the
main body of execution for the timer. This timer needs to be
scheduled on a run loop (via -[NSRunLoop addTimer:])
before it will fire.
/// - parameter: fireDate The time at which the timer should first fire.
/// - parameter: interval The number of seconds between
firings of the timer. If seconds is less than or equal to 0.0,
this method chooses the nonnegative value of 0.1 milliseconds instead
/// - parameter: repeats If YES, the timer will repeatedly reschedule itself
until invalidated. If NO, the timer will be invalidated after it fires.
/// - parameter: block The execution body of the timer;
the timer itself is passed as the parameter to this block when
executed to aid in avoiding cyclical references
- (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)interval
repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block
API_AVAILABLE(macosx(10.12), ios(10.0), watchos(3.0), tvos(10.0));
- (instancetype)initWithFireDate:(NSDate *)date interval:(NSTimeInterval)ti
target:(id)t selector:(SEL)s userInfo:(nullable id)ui repeats:(BOOL)rep
NS_DESIGNATED_INITIALIZER;
注:生成time的方式中,以“scheduledTimer”开头的方法,系统默认把timer添加到当前runloop的
NSDefaultRunLoopMode中;而以“timerWith”和“initWith”开头的方法,需要手动添加到runloop中
参数中带有“invocation”和“block”的则可以避免循环引用;而参数有target,timer则会对target强引用;
- invalidate方法会停止计时器的再次触发,并在RunLoop中将其移除。
- invalidate方法是将NSTimer对象从RunLoop中移除的唯一方法。
- 调用invalidate方法会删除RunLoop对NSTimer的强引用,以及NSTimer对target和userInfo的强引用!
NSTimer循环引用的示意图:
即使target参数使用weak修饰,timer仍然会对target强引用;
解决循环引用问题的方法:
方法一:使用系统的invalidate,废弃NSTimer对象,并置空;
方法二:给NSTimer新增category ,并预留block接口解决;
@implementation NSTimer (KT)
+ (NSTimer *)kt_scheduledTimeWithTimeInterval:(NSTimeInterval)interval block:(void(^)(void))block repeats:(BOOL)repeats{
return [self scheduledTimerWithTimeInterval:interval target:self selector:@selector(kt_blockInvoke:) userInfo:[block copy] repeats:repeats];
}
+ (void)kt_blockInvoke:(NSTimer *)timer{
void (^block)(void) = timer.userInfo;
if(block){ block();}
}
@end
//使用:
__weak KTVC * weakSelf = self;
[NSTimer kt_scheduledTimeWithTimeInterval:2.0 block:^{/*weakSelf something*/} repeats:YES];
//timer仍然需要调用invalidate,并置nil
方法三:使用GCD,自定义定时器;/*void dispatch_source_set_event_handler(dispatch_source_t source,dispatch_block_t _Nullable handler);*/
方法四:使用代理NSProxy(runtime消息转发);