swift之預防 Timer 的循環引用


預防 Timer 的循環引用

2017.03.02 02:42* 字數 584 閱讀 1098評論 0喜歡 1

在iOS開發過程中,Timer(NSTimer)是我們經常要使用的一個類。通過Timer,可以定時觸發某個事件,或者執行一些特定的操作。但是稍微不注意,就會導致內存泄漏(memory leak),而這種內存泄漏,就是循環引用引起的。例如在一個視圖控制器MyViewController

fileprivate var myTimer: Timer?

 

self.myTimer = Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(myTimerAction), userInfo: nil, repeats: true)

那么你調用profile的Leaks工具時會發現MyViewController退出之后,就會檢測到內存泄漏。如果你看Apple的開發文檔足夠細心,你將會發現問題所在:

The object to which to send the message specified by aSelector when the timer fires. The timer maintains a strong reference to target until it (the timer) is invalidated.

原來Timer調用scheduledTimer時,會強引用target,這里即是MyViewController對象。解決方法就是按照文檔所說在某個地方或時間點,手動將定時器invalidated就可以了。

self.myTimer.invalidate()

self.myTimer = nil

但是你千萬不要將上述代碼放到deinit里面(慣性思維會讓我們把釋放清除工作放到deinit里),因為循環引用之后MyViewController對象不會釋放,deinit將永遠不會被調用。你可以重載viewDidDisappear,放到里面去。或者確定不需要定時器時,及時銷毀定時器。

雖然問題得到解決,但很明顯,啰嗦且不夠優雅。所幸iOS 10.0+之后,一切變得簡單起來了……

weak var weakSelf = self

Timer.scheduledTimer(withTimeInterval: interval, repeats: true, block:{(timer: Timer) -> Void in

  weakSelf?.doSomething()

})

項目往往需要向下兼容,有沒有辦法使得iOS 10.0之前版本能夠這樣簡單的使用 block,優雅的解決循環飲用呢?答案是肯定的。

首先創建模版類保存 block:

class Block {

  let f : T

  init(_ f: T) { self.f = f }

}

Timer增加如下擴展

extension Timer {

  class func app_scheduledTimer(withTimeInterval interval: TimeInterval, repeats: Bool, block: @escaping (Timer) -> Swift.Void) -> Timer {

    if #available(iOS 10.0, *) {

      return Timer.scheduledTimer(withTimeInterval: interval, repeats: repeats, block: block)

    }

    return Timer.scheduledTimer(timeInterval: interval, target: self, selector: #selector(app_timerAction), userInfo: Block(block), repeats: repeats)

  }

 

  class func app_timerAction(_ sender: Timer) {

    if let block = sender.userInfo as? Block<(Timer) -> Swift.Void> {

      block.f(sender)

    }

  }

}

這樣就沒有了iOS版本的限制,方便快捷的使用Timer了:

weak var weakSelf = self

Timer.app_scheduledTimer(withTimeInterval: interval, repeats: true, block:{(timer: Timer) -> Void in

  weakSelf?.doSomething()

})

總結:

1、當調用Apple的API時,需要傳遞類對象self本身的,我們一定要看清文檔,self會不會被保留強引用(MAC時代的被retain);

2、當self被強引用時,像Timer一樣,增加類似的一個擴展,或者可以很好的解決問題;

3、Block模版類,或許可以很優雅的解決你所遇到的問題。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM