Swift 4 Xcode中切换容器视图时如何停止后台Timer以降低CPU占用
Hey, I’ve run into exactly this issue before! The problem here is that scheduledTimer adds the timer to the RunLoop’s common mode by default—even when your container view’s ViewController is hidden, the RunLoop keeps ticking, so the timer keeps running in the background and eats up CPU. Here are two solid solutions to fix this:
1. Use View Controller Lifecycle Methods
When you hide/show a container view, the embedded ViewController’s view lifecycle methods should trigger. You can leverage these to start/stop your timers automatically:
For ContainerA's ViewController:
var ViewATimer: Timer? override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) // Only start the timer if it doesn't already exist if ViewATimer == nil { ViewATimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(update), userInfo: nil, repeats: true) } } override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) // Stop and clean up the timer ViewATimer?.invalidate() ViewATimer = nil // Critical to avoid retain cycles and re-use issues } @objc func update() { // Your timer logic here }
For ContainerB's ViewController:
var ViewBTimer: Timer? override func viewWillAppear(_ animated: Bool) { super.viewWillAppear(animated) if ViewBTimer == nil { ViewBTimer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(secondUpdate), userInfo: nil, repeats: true) } } override func viewDidDisappear(_ animated: Bool) { super.viewDidDisappear(animated) ViewBTimer?.invalidate() ViewBTimer = nil } @objc func secondUpdate() { // Your timer logic here }
Why this works: When the container view is shown, viewWillAppear fires and starts the timer. When hidden, viewDidDisappear stops and destroys the timer, ensuring it doesn’t run in the background.
2. Manually Control Timers from the Main ViewController
If for some reason the lifecycle methods don’t trigger (e.g., you’re only toggling isHidden without modifying the view hierarchy), you can directly call methods on your child ViewControllers to start/stop timers:
Step 1: Keep references to child ViewControllers in your main VC
First, get references to the embedded ViewControllers when the segues fire:
class MainViewController: UIViewController { var viewControllerA: ViewControllerA? var viewControllerB: ViewControllerB? override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if segue.identifier == "SegueToContainerA" { // Match your segue ID viewControllerA = segue.destination as? ViewControllerA } else if segue.identifier == "SegueToContainerB" { viewControllerB = segue.destination as? ViewControllerB } } }
Step 2: Add timer control methods to child ViewControllers
In ViewControllerA:
func startTimer() { if ViewATimer == nil { ViewATimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(update), userInfo: nil, repeats: true) } } func stopTimer() { ViewATimer?.invalidate() ViewATimer = nil }
In ViewControllerB:
func startTimer() { if ViewBTimer == nil { ViewBTimer = Timer.scheduledTimer(timeInterval: 10, target: self, selector: #selector(secondUpdate), userInfo: nil, repeats: true) } } func stopTimer() { ViewBTimer?.invalidate() ViewBTimer = nil }
Step 3: Call these methods in your button tap handlers
@IBAction func showContainerA(_ sender: UIButton) { containerA.isHidden = false containerB.isHidden = true viewControllerA?.startTimer() viewControllerB?.stopTimer() } @IBAction func showContainerB(_ sender: UIButton) { containerA.isHidden = true containerB.isHidden = false viewControllerB?.startTimer() viewControllerA?.stopTimer() }
Key Notes to Avoid Issues
- Always set your timer to
nilafter callinginvalidate(): This breaks any retain cycles and ensures you don’t accidentally reuse an invalidated timer. - If you switch to closure-based timers (recommended for Swift), use
[weak self]to prevent memory leaks:ViewATimer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) { [weak self] timer in self?.update() } - Never create multiple timers without checking if an existing one exists—this would cause overlapping timers and even higher CPU usage.
内容的提问来源于stack exchange,提问作者Shahid Miah

