測試代碼是在子線程中執行的,同時執行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]表示很多年以后的未來某一天。