美文网首页
延时执行

延时执行

作者: 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