由于團隊希望項目能夠去 coredata 化,而以往狀態同步都是依賴于 coredata 的nsfetchedresultscontroller
。因此去 coredata 則必須尋找一種替代方案來進行狀態同步。
notificationcenter
狀態同步實際是一對多的場景,也就是一個事件可以被多個觀察者監聽到。而蘋果的系統框架自帶的 notificationcenter 正是用來適配這種場景,并且其也是被系統框架本身及我們開發者大面積使用的。用法如下:
- 定義通知名字,以及需要額外傳遞信息的 key
- 基于 target-action 的方式注冊通知
1
|
open func addobserver(_ observer: any, selector aselector: selector, name aname: nsnotification.name?, object anobject: any?) |
實現監聽通知的方法
1
|
func onreceivednotification(note: nsnotification) |
發送通知,可以傳遞發送通知的對象(object)以及一些額外的信息(userinfo)
1
|
open func post(name aname: nsnotification.name, object anobject: any?, userinfo auserinfo: [anyhashable : any]? = nil) |
移除注冊的通知
1
|
open func removeobserver(_ observer: any, name aname: nsnotification.name?, object anobject: any?) |
當然 notificationcenter 也提供了一種更加便利基于 block 的方式注冊監聽通知,其將 2,3 兩個步驟整合為 1 個步驟。
1
|
open func addobserver(forname name: nsnotification.name?, object obj: any?, queue: operationqueue?, using block: @escaping (notification) -> void ) -> nsobjectprotocol |
整體流程很清晰,簡單易用,但是卻有一個嚴重的缺點 —— 弱類型。我們接收到的是一個nsnotification
對象。
1
2
3
4
5
|
open class nsnotification : nsobject, nscopying, nscoding { open var name: nsnotification.name { get } open var object: any? { get } open var userinfo: [anyhashable : any]? { get } } |
假設我們需要傳遞一個關注狀態改變的信息,那么需要包含關注更改后的狀態以及被關注者的 id。那么我們需要從 userinfo 中取出所需要的值:
1
2
|
let following = notification.userinfo[ "followingkey" ] as! nsnumber let userid = notification.userinfo[ "useridkey" ] as! nsnumber; |
也就是說接收通知的一方一般需要要查看文檔才知道怎樣從 userinfo 取值,取的值的類型又是什么。這對于使用是極為不方便的。
swiftnotificationcenter
swiftnotificationcenter是一種面向協議的通知中心方案。使用方式如下:
定義協議
1
2
3
|
protocol followingchanged { func followingdidchange(following: bool , userid: nsnumber) } |
基于協議注冊通知
1
|
broadcaster. register (update.self, observer: observer) |
實現協議方法
1
2
3
4
5
|
extension viewcontroller: followingchanged { func followingdidchange(following: bool , userid: nsnumber) { // do something } } |
發送通知
1
2
3
|
broadcaster.notify(followingchanged.self) { $0.followingdidchange(following, userid) } |
移除注冊的通知
1
|
broadcaster.unregister(update.self, observer: observer) |
我們可以看到,其基于協議的方式解決了弱類型的問題,并且其通過associatedobject
實現了通知的自動移除。但其也存在著擴展性較差的問題。
依然是關注改變的場景,假如隨著業務的發展,有的地方需要知道關注后是否為互關的狀態,那么又需要增加一個字段來標識。因此我們需要修改協議,增加參數,且由于其不是必須傳遞的參數,因此是 optional 類型。
1
2
3
|
protocol followingchanging { func followingdidchange(following: bool , userid: nsnumber, followingeachother: nsnumber?) } |
如果在該類型通知被廣泛應用的場景,那么需要修改的地方就尤其多了。這顯然也是難以接受的。
eventbus
eventbus 在安卓中被廣泛地應用,其流程如下圖所示:

圖片來源:eventbus
使用方式如下:
定義事件
1
2
3
4
|
class tpfollowingchangedevent: nsobject, tpevent { private (set) var following: bool private (set) var userid: nsnumber } |
注冊事件
1
|
tpeventbus<tpfollowingchangedevent>.shared. register (eventtype: tpfollowingchangedevent.self, subscriber: self, selector: #selector(onevent(event:object:))) |
實現監聽事件的方法
1
2
3
|
@objc func onevent(event: tpfollowingchangedevent, object: any?) { // do something } |
發送事件
1
|
tpeventbus.shared.post(event: event, object: self) |
移除事件的注冊
1
|
tpeventbus<tpfollowingchangedevent>.shared.unregister(eventtype: tpfollowingchangedevent.self, subscriber: self) |
我們可以看到, eventbus 也是強類型的。
假如依然關注的場景,需要增加 followingeachother 參數,那么我們只需要在 tpfollowingchangedevent 中增加 followingeachother 參數即可。如下所示:
1
2
3
4
5
|
class tpfollowingchangedevent: nsobject, tpevent { private (set) var following: bool private (set) var userid: nsnumber private (set) var followingeachother: nsnumber? } |
因此使用 eventbus 實現了以下需求:
- 強類型
- 可擴展
eventbus 同 notificationcenter 都是基于 target-action 的方案,但是我們不難將其擴展為支持 block 監聽的方式,并且同樣讓其能夠自動移除事件的注冊。類似于如下的使用方式:
1
2
3
|
tpeventbus<tpfollowingchangedevent>.shared.subscribe(eventtype: tpfollowingchangedevent.self).forobject(self).onqueue(operationqueue.main).onevent { (event, object) in // do something }.disposed(by: self) |
基于 oc, 我實現了一個小巧但比較全面的 eventbus 供參考:tpeventbus。
最后
我們可以看到,一對多場景中觀察者模式的應用流程都大同小異,但是如何更好用確是值得深思的。當然以上也只是我在一些使用場景上的思考,肯定會欠缺考慮,歡迎拍磚:blush:。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持服務器之家。
原文鏈接:https://juejin.im/post/5d88e7176fb9a06acd456067