详解iOS中跨页面状态同步方案比较

 更新时间:2019年09月24日 09:58:11   作者:Tpphha  
这篇文章主要介绍了详解iOS中跨页面状态同步方案比较,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

由于团队希望项目能够去 CoreData 化,而以往状态同步都是依赖于 CoreData 的NSFetchedResultsController。因此去 CoreData 则必须寻找一种替代方案来进行状态同步。

NotificationCenter

状态同步实际是一对多的场景,也就是一个事件可以被多个观察者监听到。而苹果的系统框架自带的 NotificationCenter 正是用来适配这种场景,并且其也是被系统框架本身及我们开发者大面积使用的。用法如下:

  1. 定义通知名字,以及需要额外传递信息的 key
  2. 基于 target-action 的方式注册通知
open func addObserver(_ observer: Any, selector aSelector: Selector, name aName: NSNotification.Name?, object anObject: Any?)

实现监听通知的方法

func onReceivedNotification(note: NSNotification)

发送通知,可以传递发送通知的对象(object)以及一些额外的信息(userInfo)

open func post(name aName: NSNotification.Name, object anObject: Any?, userInfo aUserInfo: [AnyHashable : Any]? = nil)

移除注册的通知

open func removeObserver(_ observer: Any, name aName: NSNotification.Name?, object anObject: Any?)

当然 NotificationCenter 也提供了一种更加便利基于 block 的方式注册监听通知,其将 2,3 两个步骤整合为 1 个步骤。

open func addObserver(forName name: NSNotification.Name?, object obj: Any?, queue: OperationQueue?, using block: @escaping (Notification) -> Void) -> NSObjectProtocol

整体流程很清晰,简单易用,但是却有一个严重的缺点 —— 弱类型。我们接收到的是一个NSNotification对象。

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 中取出所需要的值:

let following = notification.userInfo["FollowingKey"] as! NSNumber
let userID = notification.userInfo["UserIDKey"] as! NSNumber;

也就是说接收通知的一方一般需要要查看文档才知道怎样从 userInfo 取值,取的值的类型又是什么。这对于使用是极为不方便的。

SwiftNotificationCenter

SwiftNotificationCenter是一种面向协议的通知中心方案。使用方式如下:

定义协议

protocol FollowingChanged {
  func followingDidChange(following: Bool, userID: NSNumber)
}

基于协议注册通知

Broadcaster.register(Update.self, observer: observer)

实现协议方法

extension ViewController: FollowingChanged {
 func followingDidChange(following: Bool, userID: NSNumber) {
  // do something
 }
}

发送通知

Broadcaster.notify(FollowingChanged.self) {
  $0.followingDidChange(following, userID)
}

移除注册的通知

Broadcaster.unregister(Update.self, observer: observer)

我们可以看到,其基于协议的方式解决了弱类型的问题,并且其通过AssociatedObject实现了通知的自动移除。但其也存在着扩展性较差的问题。

依然是关注改变的场景,假如随着业务的发展,有的地方需要知道关注后是否为互关的状态,那么又需要增加一个字段来标识。因此我们需要修改协议,增加参数,且由于其不是必须传递的参数,因此是 optional 类型。

protocol FollowingChanging {
  func followingDidChange(following: Bool, userID: NSNumber, followingEachOther: NSNumber?)
}

如果在该类型通知被广泛应用的场景,那么需要修改的地方就尤其多了。这显然也是难以接受的。

EventBus

EventBus 在安卓中被广泛地应用,其流程如下图所示:

图片来源:EventBus

使用方式如下:

定义事件

 class TPFollowingChangedEvent: NSObject, TPEvent {
  	private(set) var following: Bool
  		private(set) var userID: NSNumber
}

注册事件

TPEventBus<TPFollowingChangedEvent>.shared.register(eventType: TPFollowingChangedEvent.self, subscriber: self, selector: #selector(onEvent(event:object:)))

实现监听事件的方法

@objc func onEvent(event: TPFollowingChangedEvent, object: Any?) {
  	// do something
}

发送事件

TPEventBus.shared.post(event: event, object: self)

移除事件的注册

TPEventBus<TPFollowingChangedEvent>.shared.unregister(eventType: TPFollowingChangedEvent.self, subscriber: self)

我们可以看到, EventBus 也是强类型的。

假如依然关注的场景,需要增加 followingEachOther 参数,那么我们只需要在 TPFollowingChangedEvent 中增加 followingEachOther 参数即可。如下所示:

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 监听的方式,并且同样让其能够自动移除事件的注册。类似于如下的使用方式:

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:。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • iOS如何裁剪圆形头像

    iOS如何裁剪圆形头像

    这篇文章主要介绍了iOS如何裁剪圆形头像的方法,如何为圆形头像加边框,如何进行截图操作,感兴趣的小伙伴们可以参考一下
    2016-04-04
  • iOS中封装.framework及使用的方法详解

    iOS中封装.framework及使用的方法详解

    这篇文章主要给大家介绍了关于iOS中封装.framework及使用的相关资料,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧。
    2018-04-04
  • iOS逆向开发之微信自动添加好友功能

    iOS逆向开发之微信自动添加好友功能

    这篇文章主要介绍了iOS逆向开发之微信自动添加好友功能,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2019-04-04
  • iOS在状态栏上显示提醒信息的功能定制

    iOS在状态栏上显示提醒信息的功能定制

    这篇文章主要给大家介绍了iOS在状态栏上显示提醒信息的相关资料,实现后的效果非常不错,文中给出了详细的示例代码供大家参考学习,需要的朋友们下面来一起看看吧。
    2017-06-06
  • iOS利用UIScrollView实现无限滚动效果

    iOS利用UIScrollView实现无限滚动效果

    这篇文章主要给大家介绍了iOS如何利用UIScrollView实现无限滚动的效果,首先需要说明的是,文本所讲的是一种"笨办法",但是好理解且容易实现,在图片不多的时候用它也无妨。感兴趣的朋友们下面跟着小编一起来学习学习吧。
    2016-12-12
  • 浅谈Unity中IOS Build Settings选项的作用

    浅谈Unity中IOS Build Settings选项的作用

    下面小编就为大家分享一篇浅谈Unity中IOS Build Settings选项的作用,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-01-01
  • iOS中containsString和rangeOfString的区别小结

    iOS中containsString和rangeOfString的区别小结

    这篇文章主要给大家总结介绍了关于iOS中containsString和rangeOfString的一些区别,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2018-01-01
  • iOS获取Label高度的几种方法与对比

    iOS获取Label高度的几种方法与对比

    这篇文章主要介绍了给大家介绍了iOS获取Label高度的几种方法,包括 view的sizeThatFits 方法、view的sizeToFit方法、NSString的sizeWithAttributes方法和NSString 的 boundingRectWithSize 方法,文中不仅介绍四种方法的实现,还进行了对比,下面来一起看看吧。
    2016-11-11
  • ios利用 AFN 上传相册或者拍照图片

    ios利用 AFN 上传相册或者拍照图片

    这篇文章主要介绍了ios利用 AFN 上传相册或者拍照图片的相关资料,非常具有实用价值,需要的朋友可以参考下
    2017-06-06
  • iOS实现只有底部边框线的输入框示例代码

    iOS实现只有底部边框线的输入框示例代码

    这篇文章给大家分享了一种利用iOS实现只有底部边框线的输入框,其实这个效果也挺常见的,本文给出了示例代码,下面来看看如何实现这种效果。
    2016-09-09

最新评论