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()
})
}
网友评论