iOS 如何正确的使用NSTimer
1、属性传值循环引用
如:- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
TabelViewCell *cell = ...
cell.tableView = tableView;
return cell;
}
@interface TableViewCell: UITableViewCell
@property (nonatomic, strong) UITableView *tableView;
@end
cell 添加到tableView上被tanleView强引用,cell中tableView被强引用,造成循环引用;
所以cell中tableView应该用weak关键字
2、delegate属性用strong关键字循环引用
定义:
@interface Class A: NSObject
@property (nonatomic, strong) BView *someView;
@property (nonatomic, strong) XXXDelegate delegate;
调用:
self.someView.delegate = self;
class A强引用BView, BView的代理指向A,因为delegate是strong关键字修饰,所以BView会强引用A的实例,造成循环引用
所以delegate关键字应该用weak修饰
3、block接获变量,循环引用
self.block = ^{
self.someProperty = xxx;
}
self持有block,block截获self(这一点我也没搞太明白),赋值会把block copy到堆上,持有相关变量,就是self持有block,block又持有self,形成循环引用
解决方式:
__weak typeOf(self) weakSelf = self;
self.block = ^{
weakSelf.someProperty = xxx;
}
延展:
但这种方式可能会造成内存提前回收,比如说:block中不是简单的赋值操作,而是一个耗时的网络请求,block中的操作还没有结束,self被释放掉了,这个时候seakSelf为nil,所以someProperty无法赋值
解决方式:
__weak typeOf(self) weakSelf = self;
self.block = ^{
__strong typeOf(self) strongSelf = weakSelf;
strongSelf.someProperty = xxx;
}
原理: block中强引用self,所以self在要被释放时检查到引用计数不为0,所以系统无法释放self实例,只有等待block执行完毕,block内存被释放的时候,才会释放掉self,所以someProperty赋值成功
4、NSTimer循环引用
例如:
class ViewController
@property (nonatomic, strong) NSTimer *timer;
调用:
self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerRun) userInfo:nil repeats:YES];
这个类似于delegate,把timer的target指向了self,所以可以用weak的方式解除循环引用;
但是在实际的代码使用过程中,timer一般需要加入到runloop中,被runloop强引用,所以timer强引用viewController, runloop强引用timer,ViewController pop后无法释放,具体可以查看这篇博客对NSTimer的分析:https://www.jianshu.com/p/d4589134358a
所以可以采用另一种方式解决循环引用:
__weak typeof(self) weakSelf = self;
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
[weakSelf timerRun];
}];
注意:timer释放前需要invalidate
链接:https://www.zhihu.com/question/34123925/answer/1181846112