1期_Swift中Timer的使用

Timer的使用经常伴随着循环引用问题。本文主要如何使用以及解决循环引用

  • Timer中iOS10前后区别
  • Timer那种Api需要加入当前RunLoop

Timer改为局部变量,也会存在循环问题。就算使用scheduledTimer方法,内部默认添加到当前默认runloop中。也避免不了。更何况调用invalidate方法,进行定时的销毁

RunLoop会强持有timer。Timer持有控制器。Runloop在程序运行期间不会销毁,timer就不会自动销毁,那么Timer引用的target也不会销毁

func initScheduleTimer() {
//已经添加到runloop中,但仍旧有循环引用问题
let lineTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateLineTime), userInfo: nil, repeats: true)

}

Timer中前四种方法不推荐使用,都有循环引用问题.
iOS10之后增加了Timer的block形式,内部是没有循环引用问题,在iOS10之前存在。

使用init(timeInterval:等方法创建的Timer需要加入RunLoop中;使用scheduledTimer创建的Timer内部会自动添加

open class Timer : NSObject {
    
    //不推荐使用
    public /*not inherited*/ init(timeInterval ti: TimeInterval, invocation: NSInvocation, repeats yesOrNo: Bool)

    //不推荐使用
    open class func scheduledTimer(timeInterval ti: TimeInterval, invocation: NSInvocation, repeats yesOrNo: Bool) -> Timer

    //不推荐使用
    public /*not inherited*/ init(timeInterval ti: TimeInterval, target aTarget: Any, selector aSelector: Selector, userInfo: Any?, repeats yesOrNo: Bool)

    //不推荐使用
    open class func scheduledTimer(timeInterval ti: TimeInterval, target aTarget: Any, selector aSelector: Selector, userInfo: Any?, repeats yesOrNo: Bool) -> Timer
    
    
    @available(iOS 10.0, *)
    public /*not inherited*/ init(timeInterval interval: TimeInterval, repeats: Bool, block: @escaping @Sendable (Timer) -> Void)

    
    /// 
    @available(iOS 10.0, *)
    open class func scheduledTimer(withTimeInterval interval: TimeInterval, repeats: Bool, block: @escaping @Sendable (Timer) -> Void) -> Timer
}

iOS10之前解决循环引用

  • 建立中间类SwiftWeakProxy
func initProxyTimer() {
    let pro = SwiftWeakProxy(self)
    lineTimer = Timer.scheduledTimer(timeInterval: 1.0, target: pro, selector: #selector(updateLineTime), userInfo: nil, repeats: true)
}

class SwiftWeakProxy: NSObject {
    weak var target:NSObjectProtocol?
    public init(_ target:NSObjectProtocol?) {
        super.init()
        self.target = target
    }
    override func forwardingTarget(for aSelector: Selector!) -> Any? {
        if self.target?.responds(to: aSelector) == true {
            return self.target
        } else {
            return super.forwardingTarget(for: aSelector)
        }
    }
    
    deinit {
        print("\(self)-deinit")
    }
}

  • Timer扩展
extension Timer {
    
    /// Timer将userInfo作为callback的定时方法
    /// 目的是为了防止Timer导致的内存泄露
    /// - Parameters:
    ///   - timeInterval: 时间间隔
    ///   - repeats: 是否重复
    ///   - callback: 回调方法
    /// - Returns: Timer
    public static func zk_scheduledTimer(timeInterval: TimeInterval, repeats: Bool, with callback: @escaping () -> Void) -> Timer {
        return scheduledTimer(timeInterval: timeInterval,
                              target: self,
                              selector: #selector(callbackInvoke(_:)),
                              userInfo: callback,
                              repeats: repeats)
    }
    
    /// 私有的定时器实现方法
    ///
    /// - Parameter timer: 定时器
    @objc
    private static func callbackInvoke(_ timer: Timer) {
        guard let callback = timer.userInfo as? () -> Void else { 
            print("未实现timer.userInfo方法")
            return
        }
        callback()
    }
    
}

调用方法

func initextensionTimer()  {
        lineTimer = Timer.zk_scheduledTimer(timeInterval: 1.0, repeats: true, with: { [weak self]  in
            guard let `self` = self else { return }
            self.updateLineTime()
        })
    }
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容