看看timer最常用的写法:
@interface TimerViewController ()
@property (nonatomic, strong) NSTimer *timer;
@end
@implementation TimerViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.timer = [NSTimer timerWithTimeInterval:1.0 target:self selector:@selector(timerRun) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
}
- (void)timerRun {
NSLog(@"%s", __func__);
}
- (void)dealloc {
[self.timer invalidate];
NSLog(@"%s", __func__);
}
@end
但是当回退到上一个控制器的时候,发现并没有走当前控制器的dealloc方法,也就是说当前控制器并没有被释放,那么当前控制器强引用的timer对象也没有被释放,这就造成了内存泄漏,如下图所示。
这里的每一个箭头代表着一个强指针,当回退上一个界面时,NavigationController指向TimerViewController的强引用被销毁,但是TimerViewController和timer之间互相强引用,内存泄漏。
解决方法1:
在这里加入了一个中间代理对象LJProxy,TimerViewController不直接持有timer,而是持有LJProxy实例,让LJProxy实例来弱引用TimerViewController,timer强引用LJProxy实例,直接看代码
@interface LJWeakProxy : NSProxy
+ (instancetype)proxyWithTarget:(id)target;
@property (weak, nonatomic) id target;
@end
@implementation LJWeakProxy
+ (instancetype)proxyWithTarget:(id)target {
LJWeakProxy *proxy = [LJWeakProxy alloc];
proxy.target = target;
return proxy;
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel {
return [self.target methodSignatureForSelector:sel];
}
- (void)forwardInvocation:(NSInvocation *)invocation {
[invocation invokeWithTarget:self.target];
}
@end
Controller里只修改了下面一句代码:
- (void)viewDidLoad {
[super viewDidLoad];
// 这里的target又发生了变化
self.timer = [NSTimer timerWithTimeInterval:1.0 target:[LJWeakProxy proxyWithTarget:self] selector:@selector(timerRun) userInfo:nil repeats:YES];
[[NSRunLoop currentRunLoop] addTimer:self.timer forMode:NSDefaultRunLoopMode];
}
解决方法2:通过消息转发的方法的方式
@interface TimerViewController ()
@property (nonatomic, strong) NSTimer *timer;
@end
@implementation TimerViewController
- (void)viewDidLoad {
[super viewDidLoad];
__weak typeof(self) weakSelf = self;
self.timer = [NSTimer scheduledTimerWithTimeInterval:1.0 repeats:YES block:^(NSTimer * _Nonnull timer) {
[weakSelf timerRun];
}];
}
- (void)timerRun {
NSLog(@"%s", __func__);
}
- (void)dealloc {
[self.timer invalidate];
NSLog(@"%s", __func__);
}
解决方法3:
需要定时器在 UIScrollView 滑动时也不影响的话,有两种解决方法:
给NSTimer分别添加到UITrackingRunLoopMode 和 NSDefaultRunLoopMode这两个模式中:
[[NSRunLoop mainRunLoop] addTimer:timer
forMode:NSDefaultRunLoopMode];
[[NSRunLoop mainRunLoop] addTimer:timer
forMode: UITrackingRunLoopMode];
给NSTimer添加NSRunLoop的NSRunLoopCommonModes中,平常用这中就可以了,比较简便来:
[[NSRunLoop mainRunLoop] addTimer:timer
forMode: NSRunLoopCommonModes];
1. 用定时器有遇到什么问题吗?(是否准时?循环引用)
2. NSTimer是如何释放的?
3.如何实现一个倒计时功能,类似于蘑菇街的秒杀?
首先用NSDateFormatter获取当前时间字符串 然后使用GCD
dispatch_queue_t queue = dispatch_get_globe_queue{
dispatch_source_t *timer = dispatch_source_creat(
dispatch_source_set_timer
)
dispatch_source_set_event_handle_timer^{
}
}
4..触发的timer在滑动时会暂停回调,为什么?
5. 定时器什么时候销毁,一些通知,KVO放在哪里销毁?
答案和其他问题待更新!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
网友评论