iOS performSelector:withObject:afterDelay:不执行

测试代码是在子线程中执行的,同时执行performSelector:withObject:afterDelayperformSelector:withObject:
测试结果,带有afterDelay的方法并没有被执行:
2017-12-01 09:59:49.181574+0800 DEMO[1220:241328] ----Thread:{number = 3, name = (null)}---39

下面是有问题的测试代码

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    
    [[NSOperationQueue new] addOperationWithBlock:^{
        // 当前在子线程中,有afterDelay参数的方法不会被执行
        [self performSelector:@selector(delay) withObject:nil afterDelay:0.3];
        // 此方法会被执行
        [self performSelector:@selector(noDelay) withObject:nil];
    }];
}

- (void)afterDelay {
    NSLog(@"afterDelay:----Thread:%@---", [NSThread currentThread]);
}

- (void)noDelay {
    NSLog(@"noAfterDelay----Thread:%@---", [NSThread currentThread]);
}
@end

分析原因:

子线的runLoop需要调用[NSRunLoop currentRunLoop]手动开启并运行run方法才能运行,而performSelector:withObject:afterDelay:会在子线程中开启一个NSTimer定时器执行selector里的方法,而恰好这时是在子线程执行的performSelector:withObject:afterDelay:,所以afterDelay方法不会被执行。

解决方法:
第一种方法:
在子线程中手动开启当前线程的runLoop并运行,但是你还必须要保证你的延迟时间到时runLoop还在运行着,修改后即可解决

- (void)viewDidLoad {
    [super viewDidLoad];
    // 测试代码是在子线程中执行的
    [[NSOperationQueue new] addOperationWithBlock:^{
        NSTimeInterval afterDelay = 0.3;
        // 当前在子线程中,有afterDelay参数的方法不会被执行
        [self performSelector:@selector(delay) withObject:nil afterDelay:afterDelay];
        // 此方法会被执行
        [self performSelector:@selector(noDelay) withObject:nil];
        
        
        // 为了防止runLoop返回,给runLoop添加一些空的源,让runLoop一直运行
        CFRunLoopRef runLoop = CFRunLoopGetCurrent();
        CFRunLoopSourceContext sourceCtx = {
            .version = 0,
            .info = NULL,
            .retain = NULL,
            .release = NULL,
            .copyDescription = NULL,
            .equal = NULL,
            .hash = NULL,
            .schedule = NULL,
            .cancel = NULL,
            .perform = NULL
        };
        CFRunLoopSourceRef source = CFRunLoopSourceCreate(NULL, 0, &sourceCtx);
        CFRunLoopAddSource(runLoop, source, kCFRunLoopDefaultMode);
        
        // 子线程的runLoop需要调用run才会运行
        // 当前线程等待,但让出当前线程时间片,然后过afterDelay秒后返回
        [[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:afterDelay]];
        
    }];
}

第二种方法: 直接使用dispatch_after,它里面的定时器不受runLoop影响的

第三种方法: 在主线程执行performSelector:withObject:afterDelay:,比如:

if ([[NSThread currentThread] isMainThread]) {
            // 当前在子线程中,有afterDelay参数的方法不会被执行
            [self performSelector:@selector(delay) withObject:nil afterDelay:0.3];
        }
        else {
            dispatch_async(dispatch_get_main_queue(), ^{
                [self performSelector:@selector(delay) withObject:nil afterDelay:afterDelay];
            });
           
        }

最终怎么解决还要根据需求而定。

Other

[[NSRunLoop currentRunLoop]runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];

作用:
在当前线程中进行一次消息轮询:运行一次runLoop,在指定runloopMode下阻塞输入直到给定日期为止,[NSDate distantFuture]表示很多年以后的未来某一天。

你可能感兴趣的:(iOS performSelector:withObject:afterDelay:不执行)