由于团队希望项目能够去 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(FollowingChanged .self , observer: observer)
实现协议方法
1 2 3 4 5 extension ViewController : FollowingChanged { func followingDidChange (following: Bool, userID: NSNumber) { } }
发送通知
1 2 3 Broadcaster .notify(FollowingChanged .self ) { $0 .followingDidChange(following, userID) }
移除注册的通知
1 Broadcaster .unregister(FollowingChanged .self , observer: observer)
我们可以看到,其基于协议的方式解决了弱类型的问题,但其也存在着扩展性较差的问题。
依然是关注改变的场景,假如随着业务的发展,有的地方需要知道关注后是否为互关的状态,那么又需要增加一个字段来标识。因此我们需要修改协议,增加参数 followingEachOther,且由于它不是必须传递的参数,因此是 optional 类型。
1 2 3 protocol FollowingChanged { 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 ?) { }
发送事件
1 TPEventBus .shared.post(event: event, object: self )
移除事件的注册
1 TPEventBus <TPFollowingChangedEvent >.shared.unregister(eventType: TPFollowingChangedEvent .self , subscriber: self )
我们可以看到, EventBus 也是强类型的。
假如依然是关注的场景,需要知道互关的状态,那么我们只需要在 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.tp_eventTokenBag)
这里我实现了一个小巧但比较全面的 EventBus 供参考:TPEventBus 。
最后 我们可以看到,一对多场景中观察者模式的应用流程都大同小异,但是如何更好用确是值得深思的。当然以上也只是我在一些使用场景上的思考,肯定会欠缺考虑,欢迎拍砖😊。