• 【iOS知识汇】NSTimer 记录


    NSTimer


    NSTimer 的 scheduledTimerWithTimeInterval  方法使用时需要在主线程中使用否则不会执行的。
    
    
    
    
    1. NSRunLoopCommonModes和Timer
        当使用NSTimer的scheduledTimerWithTimeInterval方法时。事实上此时Timer会被加入到当前线程的Run Loop中,且模式是默认的NSDefaultRunLoopMode。而如果当前线程就是主线程,也就是UI线程时,某些UI事件,比如UIScrollView的拖动操作,会将Run Loop切换成NSEventTrackingRunLoopMode模式,在这个过程中,默认的NSDefaultRunLoopMode模式中注册的事件是不会被执行的。也就是说,此时使用scheduledTimerWithTimeInterval添加到Run Loop中的Timer就不会执行。
    
     
    
        所以为了设置一个不被UI干扰的Timer,我们需要手动创建一个Timer,然后使用NSRunLoop的addTimer:forMode:方法来把Timer按照指定模式加入到Run Loop中。这里使用的模式是:NSRunLoopCommonModes,这个模式等效于NSDefaultRunLoopMode和NSEventTrackingRunLoopMode的结合。(参考Apple文档)
    
    参考代码:
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        NSLog(@"主线程 %@", [NSThread currentThread]);
        //创建Timer
        NSTimer *timer = [NSTimer timerWithTimeInterval:2.0 target:self selector:@selector(timer_callback) userInfo:nil repeats:YES];
        //使用NSRunLoopCommonModes模式,把timer加入到当前Run Loop中。
        [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
    }
    
    //timer的回调方法
    - (void)timer_callback
    {
        NSLog(@"Timer %@", [NSThread currentThread]);
    }
     
    
    输出:
    
    主线程 <NSThread: 0x71501e0>{name = (null), num = 1}
    Timer <NSThread: 0x71501e0>{name = (null), num = 1}
    Timer <NSThread: 0x71501e0>{name = (null), num = 1}
    Timer <NSThread: 0x71501e0>{name = (null), num = 1}
     
    
    2. NSThread和Timer
        上面讲的NSRunLoopCommonModes和Timer中有一个问题,这个Timer本质上是在当前线程的Run Loop中循环执行的,因此Timer的回调方法不是在另一个线程的。那么怎样在真正的多线程环境下运行一个Timer呢?
    
        可以先试试NSThread。同上,我们还是会把Timer加到Run Loop中,只不过这个是在另一个线程中,因此我们需要手动执行Run Loop(通过NSRunLoop的run方法),同时注意在新的线程执行中加入@autoreleasepool。
    
    完整代码如下:
    
    - (void)viewDidLoad
    {
        [super viewDidLoad];
        
        NSLog(@"主线程 %@", [NSThread currentThread]);
        
        //创建并执行新的线程
        NSThread *thread = [[NSThread alloc] initWithTarget:self selector:@selector(newThread) object:nil];
        [thread start];
    }
    
    - (void)newThread
    {
        @autoreleasepool
        {
            //在当前Run Loop中添加timer,模式是默认的NSDefaultRunLoopMode
            [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(timer_callback) userInfo:nil repeats:YES];
            //开始执行新线程的Run Loop
            [[NSRunLoop currentRunLoop] run];
        }
    }
    
    //timer的回调方法
    - (void)timer_callback
    {
        NSLog(@"Timer %@", [NSThread currentThread]);
    }
     
    
    输出:
    
    主线程 <NSThread: 0x7118800>{name = (null), num = 1}
    Timer <NSThread: 0x715c2e0>{name = (null), num = 3}
    Timer <NSThread: 0x715c2e0>{name = (null), num = 3}
    Timer <NSThread: 0x715c2e0>{name = (null), num = 3}
     
    
    3. GCD中的Timer
    GCD中的Timer应该是最灵活的,而且是多线程的。GCD中的Timer是靠Dispatch Source来实现的。
    
    因此先需要声明一个dispatch_source_t本地变量:
    
    @interface ViewController ()
    {
        dispatch_source_t _timer;
    }
     
    
    接着通过dispatch_source_create函数来创建一个专门的Dispatch Source,接着通过dispatch_source_set_timer函数来设置Timer的参数,注意这里的时间参数有些蛋疼。
    
    开始时间的类型是dispatch_time_t,最好用dispatch_time或者dispatch_walltime函数来创建dispatch_time_t对象。如果需要Timer立即执行,可以传入dispatch_time(DISPATCH_TIME_NOW, 0)。
    
    internal和leeway参数分别表示Timer的间隔时间和精度。类型都是uint64_t。间隔时间的单位竟然是纳秒。可以借助预定义的NSEC_PER_SEC宏,比如如果间隔时间是两秒的话,那interval参数就是:2 * NSEC_PER_SEC。
    
    leeway就是精度参数,代表系统可以延时的时间间隔,最高精度当然就传0。
    
    然后通过dispatch_source_set_event_handler函数来设置Dispatch Source的事件回调,这里当然是使用Block了。
    
    最后所有dispatch_source_t创建后默认都是暂停状态的,所以必须通过dispatch_resume函数来开始事件监听。这里就代表着开始Timer。
    
     
    
    完整代码:
    
    NSLog(@"主线程 %@", [NSThread currentThread]);
    //间隔还是2秒
    uint64_t interval = 2 * NSEC_PER_SEC;
    //创建一个专门执行timer回调的GCD队列
    dispatch_queue_t queue = dispatch_queue_create("my queue", 0);
    //创建Timer
    _timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
    //使用dispatch_source_set_timer函数设置timer参数
    dispatch_source_set_timer(_timer, dispatch_time(DISPATCH_TIME_NOW, 0), interval, 0);
    //设置回调
    dispatch_source_set_event_handler(_timer, ^()
    {
        NSLog(@"Timer %@", [NSThread currentThread]);
    });
    //dispatch_source默认是Suspended状态,通过dispatch_resume函数开始它
    dispatch_resume(_timer);
     
    
    输出:
    
    主线程 <NSThread: 0x711fab0>{name = (null), num = 1}
    Timer <NSThread: 0x713a380>{name = (null), num = 3}
    Timer <NSThread: 0x713a380>{name = (null), num = 3}
    Timer <NSThread: 0x713a380>{name = (null), num = 3}
    
    原文链接:https://www.mgenware.com/blog/?p=459
  • 相关阅读:
    Go基础数据类型
    在foreach中使用distinct查找不重复记录
    DataTable,List去重复记录的方法(转载)
    ArcEngine的IMap接口(转载)
    根据Excel表格建立Shp文件(开发详解及源代码)(转载)
    axmapcontrol和mapcontrol有什么区别呢(转发)
    DataSet多表查询操作(转载)
    c#调用DLL(转载)
    wall 系列技术贴
    ArcEngine的IFeaturLayer接口(转载)
  • 原文地址:https://www.cnblogs.com/mamamia/p/13157866.html
Copyright © 2020-2023  润新知