调用Timer.scheduledTimer传参失败,目标方法未触发求助
解决Timer Selector方法未触发的问题
结合你给出的代码,我整理了几个关键的排查和修复方向:
1. 检查Selector方法的签名是否匹配
这是最容易踩坑的核心问题:使用Timer.scheduledTimer的selector模式时,系统会将Timer实例本身作为参数传递给目标方法,而不是你传入的userInfo。你的方法签名是接收Int类型参数,和系统传递的参数类型不匹配,导致方法无法被正确调用。
修复方法:修改方法签名,先接收Timer参数,再从userInfo中取出你的索引值:
@objc func pidsUsage(_ timer: Timer) { guard let indexPid = timer.userInfo as? Int else { return } // 这里处理你的业务逻辑 }
同时更新定时器的创建代码,selector改为匹配新的方法:
basicPidTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(pidsUsage(_:)), userInfo: pidsIndex, repeats: false)
2. 确认RunLoop的运行状态和模式
Timer.scheduledTimer默认会将定时器添加到当前线程的RunLoop的default模式下。如果当前线程是UI线程,当界面有滚动(比如UIScrollView、UITableView滚动)时,default模式会被暂时挂起,定时器可能延迟甚至不触发。另外,如果是在子线程创建定时器,子线程的RunLoop默认是未启动的,定时器自然不会触发。
- 如果是UI线程的场景,可以手动将定时器添加到RunLoop的
commonModes,避免被滚动操作阻塞:
basicPidTimer = Timer(timeInterval: 1, target: self, selector: #selector(pidsUsage(_:)), userInfo: pidsIndex, repeats: false) RunLoop.current.add(basicPidTimer, forMode: .common)
- 如果是子线程创建定时器,需要手动启动RunLoop(注意要避免阻塞主线程):
DispatchQueue.global().async { self.basicPidTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(self.pidsUsage(_:)), userInfo: pidsIndex, repeats: false) RunLoop.current.run() }
3. 检查Target对象的生命周期
Timer会强引用它的target对象,如果self(比如你的ViewController)在定时器触发前被释放(例如被pop、dismiss),那么Timer的目标对象不存在,方法自然无法触发。你可以在self的deinit方法中添加打印,确认是否提前被释放:
deinit { print("当前对象已被释放") // 记得在释放前销毁定时器,避免内存泄漏 basicPidTimer?.invalidate() basicPidTimer = nil }
另外,推荐使用Block形式的Timer来避免潜在的循环引用和生命周期问题,代码更简洁且不易出错:
basicPidTimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: false) { [weak self] timer in guard let self = self, let indexPid = timer.userInfo as? Int else { timer.invalidate() return } self.pidsUsage(indexPid: indexPid) } // 此时原来的pidsUsage方法不需要@objc修饰 func pidsUsage(indexPid : Int){ // 业务逻辑 }
内容的提问来源于stack exchange,提问作者djt
相关产品推荐
相关产品推荐

