Timer的使用經(jīng)常伴隨著循環(huán)引用問(wèn)題。本文主要如何使用以及解決循環(huán)引用
-
Timer中iOS10前后區(qū)別 -
Timer那種Api需要加入當(dāng)前RunLoop中
將Timer改為局部變量,也會(huì)存在循環(huán)問(wèn)題。就算使用scheduledTimer方法,內(nèi)部默認(rèn)添加到當(dāng)前默認(rèn)runloop中。也避免不了。更何況調(diào)用invalidate方法,進(jìn)行定時(shí)的銷(xiāo)毀
RunLoop會(huì)強(qiáng)持有timer。Timer持有控制器。Runloop在程序運(yùn)行期間不會(huì)銷(xiāo)毀,timer就不會(huì)自動(dòng)銷(xiāo)毀,那么Timer引用的target也不會(huì)銷(xiāo)毀
func initScheduleTimer() {
//已經(jīng)添加到runloop中,但仍舊有循環(huán)引用問(wèn)題
let lineTimer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(updateLineTime), userInfo: nil, repeats: true)
}
Timer中前四種方法不推薦使用,都有循環(huán)引用問(wèn)題.
iOS10之后增加了Timer的block形式,內(nèi)部是沒(méi)有循環(huán)引用問(wèn)題,在iOS10之前存在。
使用init(timeInterval:等方法創(chuàng)建的Timer需要加入RunLoop中;使用scheduledTimer創(chuàng)建的Timer內(nèi)部會(huì)自動(dòng)添加
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之前解決循環(huán)引用
- 建立中間類(lèi)
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")
}
}
- 對(duì)
Timer擴(kuò)展
extension Timer {
/// Timer將userInfo作為callback的定時(shí)方法
/// 目的是為了防止Timer導(dǎo)致的內(nèi)存泄露
/// - Parameters:
/// - timeInterval: 時(shí)間間隔
/// - repeats: 是否重復(fù)
/// - callback: 回調(diào)方法
/// - 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)
}
/// 私有的定時(shí)器實(shí)現(xiàn)方法
///
/// - Parameter timer: 定時(shí)器
@objc
private static func callbackInvoke(_ timer: Timer) {
guard let callback = timer.userInfo as? () -> Void else {
print("未實(shí)現(xiàn)timer.userInfo方法")
return
}
callback()
}
}
調(diào)用方法
func initextensionTimer() {
lineTimer = Timer.zk_scheduledTimer(timeInterval: 1.0, repeats: true, with: { [weak self] in
guard let `self` = self else { return }
self.updateLineTime()
})
}