RunLoop && 计时器

前言

本文主要讲解3种计时器的比较和应用方式,从runloop的理解描述开始

附上学习中的代码:

学习过程

1.RunLoop的认识

2.三种计时器的应用方式&&比较

3.总结

内容

1.RunLoop的认识(此节主要是简要总结网上描述的认识)

thread–runloop–mode–event sources,关系可以表示如下:

内循环如下

理解:1.如同C语言中的 while(1),是程序的生命力,2.主线程会自动启动,3.子线程需要自己启动,4.不允许创建,子线程在获取它时隐式创建,线程结束时销毁5.同一时间只能启动一种模式,6.runloop记录在一个全局的字典中

获取函数CFRunloopGetCurrent(void)函数获取函数

获取当前线程的runloop使用以下代码

[NSRunLoop currentRunLoop];

可以添加观察者监听线程runloop,代码如下:

 CFRunLoopObserverRef  observer = CFRunLoopObserverCreateWithHandler(CFAllocatorGetDefault(), kCFRunLoopAllActivities, YES, 0, ^(CFRunLoopObserverRef observer, CFRunLoopActivity activity) {
            switch (activity) {
                case kCFRunLoopEntry:
                    NSLog(@"即将进入runloop");
                    break;
                case kCFRunLoopBeforeTimers:
                    NSLog(@"即将处理timer---%@", weakSelf.testTimer );
                    break;
                case kCFRunLoopBeforeSources:
                    NSLog(@"即将处理input Sources");
                    break;
                case kCFRunLoopBeforeWaiting:
                    NSLog(@"即将睡眠");
                    break;
                case kCFRunLoopAfterWaiting:
                    NSLog(@"从睡眠中唤醒,处理完唤醒源之前");
                    break;
                case kCFRunLoopExit:
                    NSLog(@"退出");
                    break;
                default:
                    break;
            }
        });
        CFRunLoopAddObserver([[NSRunLoop currentRunLoop] getCFRunLoop], observer, kCFRunLoopCommonModes);

备注:如果不是监听主线程runloop,记得要启动runloop并且给线程runloop加入timer或者事件源

2.三种计时器的应用方式&&比较

当前有3种计时器的方案

1.NSTimer(最普通的计时器)

2.CADisplayLink(屏幕刷新频率)

3.dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);

3种计时器的比较截图(NSTimer和CADisplayLink加入runloop的NSRunLoopCommonModes模式)

相比较,NSTimer准确率最低,CADisplayLink和dispatch方式生成的计时器相对准确。

3种计时器使用的注意事项

一、NSTimer和CADisplayLink的target不能直接设置为self,会互相引用导致计时器和对应的控制器不释放。问题很严重!!!

处理方案:借鉴SDWebImage库的方式,代理指向可解决问题,以下是SDWeakProxy的内部代码,使用例子请看下面内容

- (instancetype)initWithTarget:(id)target {
    _target = target;
    return self;
}
+ (instancetype)proxyWithTarget:(id)target {
    return [[SDWeakProxy alloc] initWithTarget:target];
}

- (id)forwardingTargetForSelector:(SEL)selector {
    return _target;
}

- (void)forwardInvocation:(NSInvocation *)invocation {
    void *null = NULL;
    [invocation setReturnValue:&null];
}
- (NSMethodSignature *)methodSignatureForSelector:(SEL)selector {
    return [NSObject instanceMethodSignatureForSelector:@selector(init)];
}
- (BOOL)respondsToSelector:(SEL)aSelector {
    return [_target respondsToSelector:aSelector];
}
- (BOOL)isEqual:(id)object {
    return [_target isEqual:object];
}
- (NSUInteger)hash {
    return [_target hash];
}
- (Class)superclass {
    return [_target superclass];
}
- (Class)class {
    return [_target class];
}
- (BOOL)isKindOfClass:(Class)aClass {
    return [_target isKindOfClass:aClass];
}
- (BOOL)isMemberOfClass:(Class)aClass {
    return [_target isMemberOfClass:aClass];
}
- (BOOL)conformsToProtocol:(Protocol *)aProtocol {
    return [_target conformsToProtocol:aProtocol];
}
- (BOOL)isProxy {
    return YES;
}
- (NSString *)description {
    return [_target description];
}
- (NSString *)debugDescription {
    return [_target debugDescription];
}

例子:

SDWeakProxy *weakProxy = [SDWeakProxy proxyWithTarget:self];
    self.testTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:weakProxy selector:@selector(_recordTimer) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:self.testTimer forMode:NSRunLoopCommonModes];

理解:weakProxy是一个对象,这个对象指向了self!但是不是直接self引用了timer!!可以po以下weakProxy查看!!

二、3个计数器如果是控制器持有,dismiss时或者pop时最好置nil,dispatch方式的,最好能调用一下方法 dispatch_source_cancel(self.connectTimer); 保证及时释放!!!

总结

1.理解RunLoop的特性,适当时候可以使用runloop的观察者处理问题

2.理解3种定时器的使用,CALinkDisplay和dispatch_source_create方式较好,多用后者!因为NSTimer受Runloop处理事情的时候会有误差!!!

发表评论

邮箱地址不会被公开。 必填项已用*标注