测试代码是在子线程中执行的,同时执行performSelector:withObject:afterDelay
和performSelector:withObject:
测试结果,带有afterDelay的方法并没有被执行:
2017-12-01 09:59:49.181574+0800 DEMO[1220:241328] ----Thread:<NSThread: 0x1c0261f80>{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]表示很多年以后的未来某一天。