美文网首页
延时执行

延时执行

作者: a315c2a13dc5 | 来源:发表于2017-05-01 19:08 被阅读0次

    在程序当中经常需要延时执行某些操作,而常用的延时方法有四种。

    performSelector方法

    声明

    - (void)performSelector:(SEL)aSelector withObject:(nullable id)anArgument afterDelay:(NSTimeInterval)delay ;
    

    代码举例

    [self performSelector:@selector(method:) withObject:nil afterDelay:2.0] ;
    

    注意

    • 异步执行,不会阻塞当前线程。

    • 由于该方法是基于runloop的,因此必须在一个活跃的runloop中调用。主线程的runloop不会停止,因此在主线程中该方法可以正常调用;而子线程的runloop默认是关闭的,如果不手动将其激活,该方法在子线程的调用将是无效的。

    • 在主线程中调用方法

        - (void)viewDidLoad {
            [super viewDidLoad] ;
            [self performSelector:@selector(method) withObject:nil afterDelay:2.0] ;
        }
        - (void)method {
            NSLog(@"%@",[NSThread currentThread]) ;
        }
      

    打印结果为<NSThread: 0x60800007c440>{number = 1, name = main},方法调用成功。

    • 在子线程中调用方法(不手动激活)
        - (void)viewDidLoad {
            [super viewDidLoad] ;
            dispatch_async(dispatch_queue_create("test", NULL), ^{
                [self performSelector:@selector(method) withObject:nil afterDelay:2.0] ;
            }) ;
        }
        - (void)method {
            NSLog(@"%@",[NSThread currentThread]) ;
        }
    

    无打印结果,方法调用失败。

    • 在子线程中调用方法(手动激活)
        - (void)viewDidLoad {
            [super viewDidLoad] ;
            dispatch_async(dispatch_queue_create("test", NULL), ^{
                [self performSelector:@selector(method) withObject:nil afterDelay:2.0] ;
                [[NSRunLoop currentRunLoop] addPort:[NSPort port] forMode:NSDefaultRunLoopMode] ;
                [[NSRunLoop currentRunLoop] run] ;
            }) ;
        }
        - (void)method {
            NSLog(@"%@",[NSThread currentThread]) ;
        }
    

    打印结果为<NSThread: 0x608000073e40>{number = 3, name = (null)},方法调用成功。

    • 可以通过类方法cancelPreviousPerformRequestsWithTarget:cancelPreviousPerformRequestsWithTarget:selector:object:来取消执行,但是必须和其创建在同一个线程中。
    • 可以通过方法- (void)performSelectorOnMainThread:(SEL)aSelector withObject:(id)arg waitUntilDone:(BOOL)wait;确保aSelector方法会在主线程执行。
      • wait为YES,则等待当前线程执行完以后主线程才会执行aSelector方法。
      • wait为NO,则不等当前线程执行完就在主线程中执行aSelector方法。
    • 通过该方法延时执行aSelector方法时,最多只能传入一个参数并且无法获得方法的返回值。

    NSTimer定时器

    声明

    + (NSTimer *)scheduledTimerWithTimeInterval:(NSTimeInterval)ti target:(id)aTarget selector:(SEL)aSelector userInfo:(nullable id)userInfo repeats:(BOOL)yesOrNo ;
    

    代码举例

     NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(method:) userInfo:nil repeats:NO] ;
    

    注意

    • 异步执行,不阻塞线程。
    • 由于该方法是基于runloop的,因此必须在一个活跃的runloop中调用。主线程的runloop不会停止,因此在主线程中该方法可以正常调用;而子线程的runloop默认是关闭的,如果不手动将其激活,该方法在子线程的调用将是无效的。
    • 在主线程中调用方法
        - (void)viewDidLoad {
            [super viewDidLoad] ;
            NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(method) userInfo:nil repeats:NO] ;
        }
        - (void)method {
            NSLog(@"%@",[NSThread currentThread]) ;
        }
    

    打印结果为<NSThread: 0x600000261680>{number = 1, name = main},方法调用成功。

    • 在子线程中调用方法(不手动激活)
        - (void)viewDidLoad {
            [super viewDidLoad] ;
            dispatch_async(dispatch_queue_create("test", NULL), ^{
                 NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(method) userInfo:nil repeats:NO] ;
            }) ;
        }
        - (void)method {
            NSLog(@"%@",[NSThread currentThread]) ;
        }
    

    无打印结果,方法调用失败。

    • 在子线程中调用方法(手动激活)
        - (void)viewDidLoad {
            [super viewDidLoad] ;
            dispatch_async(dispatch_queue_create("test", NULL), ^{
                 NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:2.0 target:self selector:@selector(method) userInfo:nil repeats:NO] ;
                [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSDefaultRunLoopMode] ;
                [[NSRunLoop currentRunLoop] run] ;
            }) ;
        }
        - (void)method {
            NSLog(@"%@",[NSThread currentThread]) ;
        }
    
    

    打印结果为<NSThread: 0x6000002615c0>{number = 3, name = (null)},方法调用成功。

    • 可以通过-(void)invalidate:方法取消执行,但是必须和其创建在同一个线程中。
    • NSTimer定时器存在内存泄漏的风险。通过NSTimer定时器生成的timer会被NSRunLoop对象一直持有,直到调用invalidate方法。而timer又持有target对象,如果不调用invalidate方法,target对象将会一直无法被释放,从而造成内存泄漏。

    NSThread的sleep方法

    声明

    + (void)sleepForTimeInterval:(NSTimeInterval)ti ;
    

    代码举例

    [NSThread sleepForTimeInterval:2.0] ; 
    [self method] ;
    

    注意

    • 同步执行,会阻塞线程。
    • 在主线程和子线程都可以执行。

    GCD

    声明

    void dispatch_after(dispatch_time_t when, dispatch_queue_t queue, dispatch_block_t block) ;
    

    代码举例

    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        [self method] ;
    });
    

    注意

    • 异步执行,不会阻塞线程。
    • 在主线程和子线程都可以执行。
    • 一旦执行就无法撤销。
    • 系统会帮助处理线程级的逻辑,并且调用的对象也不会被强行持有,这样就不会存在内存泄漏的问题。

    相关文章

      网友评论

          本文标题:延时执行

          本文链接:https://www.haomeiwen.com/subject/vihqtxtx.html