NSTimer的理解

NSTimer的定义

A timer that fires after a certain time interval has elapsed, sending a specified message to a target object.
简单理解就是在一定的时间间隔后向指定对象发送指定消息。

NSTimer的调用方式

一、自动加入NSRunLoop

方法名以scheduled开头的均不需要手动加入NSRunLoop
方法有三个:

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)yesOrNo;

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti invocation:(NSInvocation *)invocation repeats:(BOOL)yesOrNo;

+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block

二、需要手动加入NSRunLoop

+ (NSTimer *)timerWithTimeInterval:(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;

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block

重点参数解析

参数:target

The object to which to send the message specified by aSelector when the timer fires. 
The timer maintains a strong reference to this object until it (the timer) is invalidated.

当timer开启时,消息的reciver。NSTimer会强引用target直到(the timer) invalidated.

参数:userInfo

Custom user info for the timer.

The timer maintains a strong reference to this object until it (the timer) is invalidated. This parameter may be nil.

自定义的额外数据。和target一样,NSTimer会强引用userInfo直到(the timer) invalidated.

参数:repeats

If YES, the timer will repeatedly reschedule itself until invalidated. If NO, the timer will be invalidated after it fires.

repeats为YES时,会重复执行selector方法直到timer invalidated为止。
repeats为NO时, timer会在fires后被置为invalidated。
注意:以上说明,repeats为YES时,要想截止timer,需要调用[timer invalidate]方法,而repeats为NO时,系统自动在fires后调用[timer invalidate]方法。

- (void)invalidate;

Stops the timer from ever firing again and requests its removal from its run loop.

停止timer,并请求从其运行循环中删除它。

This method is the only way to remove a timer from an [NSRunLoop
](apple-reference-documentation://hclPs8uY7g) object. The NSRunLoop
 object removes its strong reference to the timer, either just before the [invalidate
](apple-reference-documentation://hcnIKt1Jb9) method returns or at some later point.
If it was configured with target and user info objects, the receiver removes its strong references to those objects as well.

该方法是从NSRunLoop对象中删除timer的唯一方法。 NSRunLoop对象删除其对定时器的强引用,就在invalidate方法返回之前或稍后一点。
如果配置了target和userInfo对象,timer也会删除其对这些对象的强引用。

常说的循环引用

1, self强引用timer对象。

即如下方式:

self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerSelector) userInfo:nil repeats:YES];

对象引用图:

6B6B7362-DC06-41AE-AA1C-BF9ABCDD28AF.png

执行了invalidate方法后:timer--->target, runloop--->timer 之间的强引用被删除。也就不会再有循环引用了。(当repeats参数为NO时,timer 触发后,这两个强引用就自动被删除了)。

CA10278D-CD87-4C75-BC3C-280AE3403CE8.png

所以如果repeats为YES,并且没有执行invalidate方法时,形成了循环引用,这样self就无法释放,从而内存泄漏。(有同学在self的dealloc方法中进行[timer invalidate]调用是无效果的,因为dealloc方法根本不会执行。)

2,self没有强引用timer的情况
F165D713-F6FE-4DF1-A07A-48AF181C450B.png

执行了invalidate方法后:timer--->target, runloop--->timer 之间的强引用被删除。也就不会再有循环引用了。(当repeats参数为NO时,timer 触发后,这两个强引用就自动被删除了)。

FA9FD847-7BC4-41EB-9355-F29FC09B931B.png

所以如果repeats为YES,并且没有执行invalidate方法时,虽然没有循环引用,但是runloop强引用timer,timer强引用self(target),由于(主线程中)runloop是程序运行期永久存在的,所以timer无法释放,self(target)也就无法释放,从而内存泄漏。

系统方法中有一个方法不会导致self(target)无法释放。如下:

+ (NSTimer *)timerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block

该方法没有参数targe,所以不会强引用self(target), 所以self(target)可以释放,但是如果没有调用invalidate方法, 所创建的timer也是无法释放的,所以依旧会内存泄漏。

综上所诉,当参数repeats为YES时,必须在不使用timer的时候调用invalidate方法删除相关强引用,以免内存泄漏。

避免循环引用的方式:

1,使用另一个对象,timer强引用newObjc,newObjc弱引用target。

github上已经有了代码:YYWeakProxy
对象引用图:

E606D9DA-C690-4BB7-A00D-00EFE654E8E8.png

这样timer就不再强引用self了,可以在self的dealloc中执行invalidate方法释放timer--->yyweakproxy, runloop----->timer的强引用.

原理:YYWeakProxy中weak属性target,并重写forwardingTargetForSelector方法,直接返回_target.

- (id)forwardingTargetForSelector:(SEL)selector {
    return _target;
}
2,将target设置为类对象。

对象引用图

B8EFCD0B-B320-40C4-BFB6-4F8210FCB831.png

虽然依旧有强引用:timer--->NSTimer类对象,但是由于类对象不需要回收,所以没有内存泄漏问题。
实现方式:

@interface NSTimer (BlocksSupport)
+(NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                     block:(void(^)())block
                                   repeats:(BOOL)repeats;
@end
@implementation NSTimer (BlocksSupport)
+(NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval
                                     block:(void(^)())block
                                   repeats:(BOOL)repeats
{
return [self scheduledTimerWithTimeInterval:interval
                                      target:self
                                    selector:@selector(blockInvoke:)
                                    userInfo:[block copy]
                                     repeats:repeats];
}
+(void)blockInvoke:(NSTimer *)timer {
void (^block)() = timer.userinfo;
if(block) {
    block();
}
}
@end
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 一、什么是NSRunLoop NSRunLoop是消息机制的处理模式 NSRunLoop的作用在于有事情做的时候使...
    呦释原点阅读 713评论 0 2
  • NSTimer是iOS最常用的定时器工具之一,在使用的时候常常会遇到各种各样的问题,最常见的是内存泄漏,通常我们使...
    bomo阅读 1,339评论 0 7
  • 创建NSTimer 创建NSTimer的常用方法是: + (NSTimer *)scheduledTimerWit...
    LanWor阅读 1,473评论 0 2
  • 一、什么是NSTimer 官方给出解释是“A timer provides a way to perform a ...
    行走的菜谱阅读 1,235评论 0 4
  • 数据内容:在肿瘤测序数据中,发现血系变异与体细胞变异的叠加 数据来源:TCGA 数据理解: ①TCGA数据产生过程...
    union0402阅读 848评论 0 0

友情链接更多精彩内容