返回

iOS 开发中 CADisplayLink 与 NSTimer 的使用注意事项

IOS

iOS 中的 CADisplayLink 和 NSTimer

CADisplayLink 和 NSTimer 都是 iOS 中用于安排和执行任务的定时器机制。CADisplayLink 专为与显示器刷新率同步而设计,使其成为动画和图形渲染的理想选择。而 NSTimer 提供了一个更通用的计时器,可用于各种任务。

强引用问题

在使用 CADisplayLink 和 NSTimer 时,一个常见的问题是强引用。当一个对象持有对另一个对象的强引用时,后一个对象将无法被释放,即使它不再被需要。这可能导致内存泄漏,最终使应用程序崩溃。

在 CADisplayLink 和 NSTimer 的情况下,如果没有正确处理,强引用问题可能会发生在以下情况下:

  • 当使用 CADisplayLink 时,如果在控制器 (UIViewController) 的 dealloc 方法中未显式停止定时器,则该控制器将对定时器保持强引用,即使它已被注销。
  • 当使用 NSTimer 时,如果在 target 对象的 dealloc 方法中未显式失效定时器,则该 target 对象将对定时器保持强引用,即使它已被注销。

避免强引用问题的解决方案

为了避免强引用问题,必须在不再需要定时器时显式停止或失效它。以下是推荐的解决方案:

  • 对于 CADisplayLink:
    • 在控制器的 dealloc 方法中调用 invalidate() 方法。
    • 使用弱引用代理来持有对定时器的引用,以避免强引用循环。
  • 对于 NSTimer:
    • target 对象的 dealloc 方法中调用 invalidate() 方法。
    • 使用 NSRunLoopWeakReference 类来持有对目标对象的弱引用,以避免强引用循环。

示例代码:

使用 CADisplayLink 的示例:

class ViewController: UIViewController {
    private var displayLink: CADisplayLink!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 使用弱引用代理来避免强引用循环
        displayLink = CADisplayLink(target: WeakProxy(target: self), selector: #selector(update))
        displayLink.add(to: .current, forMode: .common)
    }
    
    override func dealloc() {
        super.dealloc()
        
        displayLink.invalidate()
    }
    
    @objc private func update() {
        // 定时器代码
    }
}

class WeakProxy {
    weak var target: AnyObject?
    
    init(target: AnyObject) {
        self.target = target
    }
}

使用 NSTimer 的示例:

class ViewController: UIViewController {
    private var timer: NSTimer!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // 使用 NSRunLoopWeakReference 来避免强引用循环
        timer = NSTimer(timeInterval: 1, target: NSRunLoopWeakReference(target: self), selector: #selector(update), userInfo: nil, repeats: true)
        RunLoop.mainRunLoop().addTimer(timer, forMode: .common)
    }
    
    override func dealloc() {
        super.dealloc()
        
        timer.invalidate()
    }
    
    @objc private func update() {
        // 定时器代码
    }
}

结论

通过正确理解和处理 CADisplayLink 和 NSTimer 的使用,iOS 开发者可以避免强引用问题并编写健壮、高效的代码。通过遵循本文提供的指南和示例代码,开发者可以确保定时器在不再需要时被正确停止或失效,从而防止内存泄漏和确保应用程序的稳定性。