iOS小知识之NSTimer的循环引用一
小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
1.NSTimer的循环引用
1.1 常见问题
日常开发中,经常会用到NSTimer
定时器,一些不正确的写法,会导致NSTimer
造成循环引用,如下代码所示
``` - (void)viewDidLoad { [super viewDidLoad];
self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(fireHome) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
- (void)fireHome{
num++;
NSLog(@"hello word - %d",num);
}
- (void)dealloc{
[self.timer invalidate];
self.timer = nil;
}
``
上述案例,一定会产生循环引用
- 创建
NSTimer时,将
self传入
target,导致
NSTimer持有
self,而
self又持有
timer- 使用
NSTimer的
invalidate方法,可以解除
NSTimer对
self的持有
- 但是案例中,
NSTimer的
invalidate方法,由
UIViewController的
dealloc方法执行。但是
self被
timer持有,只要
timer有效,
UIViewController的
dealloc`方法就不会执行。故此双方相互等待,造成循环引用
1.2 target传入弱引用对象
对NSTimer
的target
参数传入一个弱引用的self
,能否打破对self
的强持有?
代码如下:
``` - (void)viewDidLoad { [super viewDidLoad];
__weak typeof(self) weakSelf = self;
self.timer = [NSTimer timerWithTimeInterval:1 target:weakSelf selector:@selector(fireHome) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
``
- 肯定是不行的,因为在
timerWithTimeInterval内部,使用强引用对象接收
target`参数,所以外部定义为弱引用对象没有任何意义
这种方式类似于以下代码:
__weak typeof(self) weakSelf = self;
typeof(self) strongSelf = weakSelf;
在官方文档中,对target
参数进行了明确说明:
- target
:定时器触发时指定的消息发送到的对象。计时器维护对该对象的强引用,直到它(计时器)失效
和Block的区别: Block将捕获到的弱引用对象,赋值给一个强引用的临时变量,当Block执行完毕,临时变量会自动销毁,解除对外部变量的持有。
2.常规解决方案
2.1 更换API
使用携带Block
的方法创建NSTimer
,避免target
的强持有
+ (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)interval repeats:(BOOL)repeats block:(void (^)(NSTimer *timer))block;
2.2 在适当时机调用invalidate
根据业务需求,可以将NSTimer
的invalidate
方法写在viewWillDisappear
方法中
``` - (void)viewWillAppear:(BOOL)animated{ [super viewWillAppear:animated];
self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(fireHome) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
} - (void)viewWillDisappear:(BOOL)animated{ [super viewWillDisappear:animated];
[self.timer invalidate];
self.timer = nil;
}
``
或者写在
didMoveTo ParentViewController`方法中
- (void)viewDidLoad {
[super viewDidLoad];
self.timer = [NSTimer timerWithTimeInterval:1 target:self selector:@selector(fireHome) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSRunLoopCommonModes];
}
- (void)didMoveToParentViewController:(UIViewController *)parent{
if (parent == nil) {
[self.timer invalidate];
self.timer = nil;
}
}
- iOS之数据结构与算法面试题2
- iOS之字符串拷贝
- iOS之动态库与静态库的实战配置
- iOS之Dispatch Semaphore与NSThread runloop实现常驻线程
- iOS之xcconfig编写指南
- iOS之OC与JS的交互(iOS与H5混编)
- iOS之认识Shell2-常用的命令参考1
- iOS之认识Shell-1
- iOS开发中Mach-O的体积优化2
- iOS开发中Mach-O的体积优化
- iOS中runtime如何实现weak变量的自动置nil和SideTable是什么?
- iOS中的循环引用
- iOS之网络相关4:TCP和UDP
- iOS之网络相关3:HTTPS
- iOS之网络相关2:HTTP的通信过程
- iOS之网络相关1:HTTP协议
- iOS逆向探究3:状态寄存器
- iOS逆向探究2:汇编初探2
- iOS逆向探究1:汇编初探1
- iOS小知识之底层问题探索