串行队列中performSelectorWithDelay调用失效的原因及解决方法
问题原因分析
你遇到的这个问题核心在于 performSelector:withObject:afterDelay: 对RunLoop的依赖,具体拆解如下:
- 这个方法的本质是把目标方法的执行请求,添加到当前线程的RunLoop中,等待指定延迟时间后由RunLoop调度执行。
- 你通过
dispatch_async(serialQueue, ...)提交的代码块,会被GCD分配到一个全新的后台线程(自定义串行队列默认属于后台队列类型)。而后台线程默认是没有启动RunLoop的——当代码块里的逻辑执行完(也就是调用完performSelector...之后),这个线程就会立即退出,RunLoop根本没机会运行,自然触发不了延迟的fetchConfigFromNetwork方法。 - 主队列对应的主线程,系统已经默认启动并持续维持着RunLoop,所以
performSelector...的延迟任务能被正常调度执行。
解决方案
根据你的业务需求,有三种靠谱的解决方式:
方式一:用GCD的dispatch_after替代performSelectorWithDelay(推荐)
GCD的延迟执行不依赖RunLoop,直接由GCD调度器管理,适配所有队列类型,稳定性更高。代码替换如下:
dispatch_time_t delayTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(rootConfig.waitTime * NSEC_PER_SEC)); dispatch_after(delayTime, serialQueue, ^{ [self fetchConfigFromNetwork]; });
方式二:手动启动自定义队列线程的RunLoop(仅当必须维持线程存活时使用)
如果你的业务需要这个串行队列对应的线程一直存活,处理延迟/重复任务,可以在代码块中手动启动RunLoop。注意要设置合理的退出条件,避免线程泄漏:
dispatch_async(serialQueue, ^(void) { // 提交延迟任务 [self performSelector:@selector(fetchConfigFromNetwork) withObject:nil afterDelay:rootConfig.waitTime]; // 启动RunLoop,仅处理默认模式的事件 [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; // 可选:在fetchConfigFromNetwork方法末尾添加停止RunLoop的逻辑,避免线程永久存活 // [[NSRunLoop currentRunLoop] stop]; });
方式三:若允许在主线程执行,直接用主队列
如果fetchConfigFromNetwork方法本身没有 heavy 操作(比如只是简单的网络请求回调处理),直接使用主队列即可,这也是你之前验证可行的方式:
dispatch_async(dispatch_get_main_queue(), ^(void) { [self performSelector:@selector(fetchConfigFromNetwork) withObject:nil afterDelay:rootConfig.waitTime]; });
内容的提问来源于stack exchange,提问作者prabodhprakash
相关产品推荐
相关产品推荐

