NSTimer
NSTimer是日常常用控件,但在使用中需要注意避免循环引用的问题。NSTimer使用方式如下1
2
3
4
5
6
7
8- (void)initTimer {
self.timer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:@selector(beginTimer) userInfo:nil repeats:YES];
}
- (void)dealloc {
[self.timer invalidate];
self.timer = nil;
}
由于timer与self相互强引用,导致走不到dealloc方法,造成内存泄露。
以下为解决NSTimer循环引用的几种方法。
选择合适时机手动释放
1 | -(void)viewDidDisappear:(BOOL)animated |
这种方案在有些情况下是不合理的,比如控制器push到下一个页面,返回时需要重新实例化timer。
在NSTimer分类中添加类方法
1 | @implementation NSTimer (BlockTimer) |
设置timer的target为自身,解决了循环引用的问题。使用时要注意block引起的循环引用。
1 | __weak typeof(self) weakSelf = self; |
给target添加中间件NSProxy
在YYKit中,作者使用YYWeakProxy类作为NSTimer、CADisplayLink 的target,使得Timer得引用变为 VC->NSTimer->YYWeakProxy–>VC,YYWeakProxy弱引用VC,当触发timer的方法时,通过消息转发机制将消息转发至VC,避免了循环引用。
使用方式在头文件中可以看到: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
35
36
37
38
39
40
41
42
43
/**
A proxy used to hold a weak object.
It can be used to avoid retain cycles, such as the target in NSTimer or CADisplayLink.
sample code:
@implementation MyView {
NSTimer *_timer;
}
- (void)initTimer {
YYWeakProxy *proxy = [YYWeakProxy proxyWithTarget:self];
_timer = [NSTimer timerWithTimeInterval:0.1 target:proxy selector:@selector(tick:) userInfo:nil repeats:YES];
}
- (void)tick:(NSTimer *)timer {...}
@end
*/
@interface YYWeakProxy : NSProxy
/**
The proxy target.
*/
@property (nullable, nonatomic, weak, readonly) id target;
/**
Creates a new weak proxy for target.
@param target Target object.
@return A new proxy object.
*/
- (instancetype)initWithTarget:(id)target;
/**
Creates a new weak proxy for target.
@param target Target object.
@return A new proxy object.
*/
+ (instancetype)proxyWithTarget:(id)target;
@end
1 |
|
使用GCD timer
以下为YYTimer,实现了线程安全的GCD timer,不会被runloop的mode影响。
1 | /** |
1 |
|
更精确的Timer
纳秒级精度的Timer1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22static const uint64_t NANOS_PER_USEC = 1000ULL;
static const uint64_t NANOS_PER_MILLISEC = 1000ULL * NANOS_PER_USEC;
static const uint64_t NANOS_PER_SEC = 1000ULL * NANOS_PER_MILLISEC;
static mach_timebase_info_data_t timebase_info;
static uint64_t nanos_to_abs(uint64_t nanos) {
return nanos * timebase_info.denom / timebase_info.numer;
}
void waitSeconds(int seconds) {
mach_timebase_info(&timebase_info);
uint64_t time_to_wait = nanos_to_abs(seconds * NANOS_PER_SEC);
uint64_t now = mach_absolute_time();
mach_wait_until(now + time_to_wait);
}