我們經(jīng)常遇到這種情況,就是一個(gè)應(yīng)用中有某些變量可能需要經(jīng)常被改變,而很多其他模塊都需要用到這個(gè)變量,我們希望在這個(gè)變量被改變時(shí),使用者能夠及時(shí)知曉并進(jìn)行更新(執(zhí)行例如 UI 刷新、重載等操作)。傳統(tǒng)一點(diǎn)的做法我們可能會(huì)去使用NSNotificationCenter
這個(gè)類,它確實(shí)非常實(shí)用,可以輕松做到類似 PubSub 的功能,但是如果有許多變量,我們豈不是要聲明許多 Notification?這顯然是低效且不現(xiàn)實(shí)。
本文將利用 Swift 的語言特效一步步打造一個(gè)十分好用的觀察者模式。
石器時(shí)代
使用泛型封裝變量,然后給出一個(gè)閉包數(shù)組,當(dāng)變量變化時(shí)遍歷閉包數(shù)組,把新值傳過去:
import Foundation
class UZObservable<T> {
var value: T? {
set(newValue) {
self._value = newValue
self.notify()
}
get {
return self._value
}
}
typealias Observer = T? -> Void
private var _value: T?
private var observers = [Observer]()
func notify() {
self.observers.forEach { $0(self.value) }
}
func bindObserver(observer: Observer) {
self.observers.append(observer)
}
}
很簡單,沒什么可解釋的。注意一點(diǎn),閉包需要用 typealias 指派一個(gè)類型,不然編譯器不認(rèn)...
隨手?jǐn)]一個(gè)測試界面,我們來看看效果。
然后是使用代碼:
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
message.bindObserver({ [weak self] (message: String?) in
self?.label.text = message
})
}
不出意外的,我們初步實(shí)現(xiàn)了觀察者模式。
鐵器時(shí)代
但是,但是。隨著我們的項(xiàng)目越來越大,一個(gè)變量的觀察者列表會(huì)越來越龐大,然而有許多觀察者實(shí)際已經(jīng)被釋放了,所以我們必須要有一個(gè)方法來處理冗余的觀察者。于是,我們可以使用 Dictionary
,也就是一個(gè)鍵對應(yīng)一個(gè)觀察者閉包,當(dāng)觀察者被釋放的時(shí)候把相應(yīng)的鍵移除掉。
于是代碼變成這樣:
import Foundation
class UZObservable<T> {
var value: T? {
set(newValue) {
self._value = newValue
self.notify()
}
get {
return self._value
}
}
typealias Observer = T? -> Void
private var _value: T?
private var observers = [String:Observer]()
func notify() {
for (_, observer) in self.observers {
observer(self.value)
}
}
func bindObserver(observer: Observer, forKey key: String) {
self.observers[key] = observer
}
func removeObserverForKey(key: String) {
self.observers.removeValueForKey(key)
}
}
看起來也蠻簡單,但是問題又產(chǎn)生了...字符串化的 key 是十分容易碰撞的,好了,這個(gè)方法顯然也是不行的。
信息化時(shí)代
看來我們需要對觀察者進(jìn)行一番修改了,為何不用一個(gè)引用對象當(dāng)做 key 呢?也就是說當(dāng)一個(gè)對象需要觀察這個(gè)變量,這個(gè)對象就把 weak 的 self
但做 key 添加到觀察者列表中,然后我們定期檢測這個(gè)對象是否為nil
,如果是就把它剔除掉。這顯然更加完美,因?yàn)槲覀冊俨挥媒g盡腦汁地去想各種奇怪的 key 了。
實(shí)現(xiàn)如下:
import Foundation
class UZObserver<T> {
var closure: (T? -> Void)!
weak var reference: AnyObject?
}
class UZObservable<T> {
var value: T? {
set(newValue) {
self._value = newValue
self.notify()
}
get {
return self._value
}
}
private var _value: T?
private var observers = [UZObserver<T>]()
func notify() {
observers.forEach { if $0.reference != nil { $0.closure(self.value) } }
}
func bindObserver(observer: AnyObject, closure: T? -> Void) {
self.clean()
let _observer = UZObserver<T>()
_observer.closure = closure
_observer.reference = observer
self.observers.append(_observer)
}
func clean() {
self.observers = self.observers.filter {
return $0.reference != nil
}
}
}
這里,每次添加新觀察者時(shí)都清理一遍無效的觀察者,這樣就可以保證可觀的內(nèi)存使用。
上面這段代碼十分穩(wěn)定,大家可以放心地在項(xiàng)目中去使用??