创建一个常用的Timer
var timer: Timer?
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer.init(timeInterval: 1, target: self, selector: #selector(timerFire), userInfo: nil, repeats: true)
RunLoop.current.add(timer!, forMode: .common)
}
@objc func timerFire(){
print("timerFire")
}
deinit {
print("deinit")
}
这段代码我们都知道因为self强引用了timer,而timer的target又强引用了self,循环引用,导致timer无法释放,不走析构函数。这时候我们思考在什么时候让timer释放最合适呢?下面有几种解决方法
第一种:didMove
当一个子对象被从父对象中移除时,父对象就会为nil,所以在这个方法中手动销毁
override func didMove(toParent parent: UIViewController?) {
super.didMove(toParent: parent)
if parent == nil {
timer?.invalidate()
timer = nil
}
}
当页面离开时,我们看见timer停止了,也走了析构函数
第二种:使用Timer的block
@available(iOS 10.0, *)
public /*not inherited*/ init(timeInterval interval: TimeInterval, repeats: Bool, block: @escaping (Timer) -> Void)
override func viewDidLoad() {
super.viewDidLoad()
timer = Timer.init(timeInterval: 1, repeats: true, block: { (timer) in
print("\(timer)")
})
RunLoop.current.add(timer!, forMode: .common)
}
deinit {
timer?.invalidate()
timer = nil
print("deinit")
}
使用这个初始化方法,就会来到我们的析构函数,进而对timer进行销毁,但是我们也看见了,该方法是在iOS 10.0以后才生效。
第三种:中介者模式
self -> timer -> self, 想打破循环圈,我们可以借助中介着让target弱引用self
首先我们先来创建一个继承NSObject的类MProxy
class MProxy: NSObject {
weak var target: NSObjectProtocol?
var sel: Selector?
var MTimer: Timer? = nil
override init() {
super.init()
}
func M_Timer(timeInterval ti: TimeInterval, target aTarget: Any, selector aSelector: Selector, userInfo: Any?, repeats yesOrNo: Bool) {
MTimer = Timer.init(timeInterval: ti, target: self, selector: aSelector, userInfo: userInfo, repeats: yesOrNo)
RunLoop.current.add(MTimer!, forMode: .common)
self.target = aTarget as? NSObjectProtocol
self.sel = aSelector
guard target?.responds(to: sel) == true else {
return
}
let method = class_getInstanceMethod(classForCoder, #selector(MTimerFire))
class_replaceMethod(classForCoder, sel!, method_getImplementation(method!), method_getTypeEncoding(method!))
}
@objc func MTimerFire(){
if target != nil{
target!.perform(self.sel)
}else{
MTimer?.invalidate()
MTimer = nil
}
}
override func forwardingTarget(for aSelector: Selector!) -> Any? {
if self.target?.responds(to: self.sel) == true {
return self.target
}
else{
print("没有方法实现")
return super.forwardingTarget(for: aSelector)
}
}
deinit {
print("\(self) 走了")
}
}
- 首先我们声明几个属性,用来保存外界的
target和sel,因为要弱引用target,所以用weak修饰。 - 模仿系统的方法,写一个自己的
Timer,内部实现仍然调用系统的。target直接引用当前类 - 获取到外界的
sel做方法做方法交换,内部实现MTimerFire方法。如果target还在,就会调用外面的方法,如果释放了就销毁当前的MTimer。 VC的调用方法
override func viewDidLoad() {
super.viewDidLoad()
proxy.M_Timer(timeInterval: 1, target: self, selector: #selector(timerFire), userInfo: nil, repeats: true)
}
当离开界面的时候输出:
timerFire
timerFire
timerFire
deinit
<SwiftDemo.MProxy: 0x600003386900> 走了
中介着模式使各个对象不需要显示地相互引用,降低耦合性,从而优雅的解决问题。