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

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

(0)

相关推荐

  • iOS点击推送消息跳到应用指定页面方法

    现在的推送用的越来越频繁,几乎每个应用都开始用到了.其实又有几个用户会去看推送消息呢?没办法,产品经理最大啊,只是苦了我们这一帮程序员啊!闲话少说,进入正题.兄弟我用的是极光推送,自然是以极光推送为例了. 现在点击推送消息,有两种跳转方式:1.打开应用,跳转到应用首页:2.打开应用,跳转到指定页面. ​第一种,你什么都不用设置,只要注册极光应用就可以.这里就不写怎么注册极光应用了,可以参考官方文档,写的很详细. 第二种,重头戏来了. // APP未运行时获取通知的内容 remoteNotific

  • iOS开发之路--微博新特性页面

    BeyondAppDelegate.m // // BeyondAppDelegate.m // 20_帅哥no微博 // // Created by beyond on 14-8-3. // Copyright (c) 2014年 com.beyond. All rights reserved. // #import "BeyondAppDelegate.h" #import "BeyondViewController.h" #import "NewFe

  • ios App加载本地HTML网页,点击网页链接跳转到app页面的方法

    一.如何在APP里加载本地html文件内容: 首先准备一个html文件,比如内容如下: <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <meta http-equiv="Content-Style-Type" content="text/css"> <tit

  • iOS项目开发--实现类似淘宝详情页面

    前段时间公司在研发一个电商项目,趁现在有时间把其中的知识点整理整理. 项目的商品详情页面当时是仿制淘宝的,用到的第三方库是MJRefresh,上拉操作和下拉操作的刷新效果是把MJRefresh刷新效果从新建个分类封装了一下,感谢杰哥!!! 基本思路: 1.设置一个 UIScrollView 作为视图底层,并且设置分页为两页 2.然后在第一个分页上添加一个 UITableView 并且设置表格能够上提加载(上拉操作即为让视图滚动到下一页) 3. 在第二个分页上添加一个 UIScrollView 并

  • 解决ios微信页面回退不刷新的问题

    在回退后需要刷新的页面加以下js $(function () { var isPageHide = false; window.addEventListener('pageshow', function () { if (isPageHide) { window.location.reload(); } }); window.addEventListener('pagehide', function () { isPageHide = true; }); }) 以上这篇解决ios微信页面回退不刷

  • iOS应用开发中实现页面跳转的简单方法笔记

    作为新手写的笔记,方便自己记忆: 从android转过来iOS的,对于页面的跳转,找了很多资料,现在记录一下页面跳转的方法. 1.用navigationController 2.直接跳(刚刚在网上找到的,不太熟,有错莫怪) 1.建一个RootViewController,在delegate.h 复制代码 代码如下: @property (strong, nonatomic) UIViewController *viewController; @property (strong, nonatomi

  • 在IOS系统上滚动条滚动到指定的位置出现空白页面的解决方案

    原因: -webkit-overflow-scrolling:touch 解释: 由于使用-webkit-overflow-scrolling这个属性,苹果手机会使用硬件加速,从而促使页面滑动得更加流畅,然而也导致了页面出现空白的情况. 解决办法: 滚动之前,先设-webit-overflow-scrolling的属性值为auto,然后页面滚动完了,再设为touch即可. 实例: $("#id").css('-webkit-overflow-scrolling','auto'); $(

  • iOS 页面滑动与标题切换颜色渐变的联动效果实例

    话不多说,直接上图,要实现类似如下效果. 这个效果非常常见,这里着重讲讲核心代码 封装顶部的PageTitleView 封装构造函数 封装构造函数,让别人在创建对象时,就传入其实需要显示的内容 frame:创建对象时确定了 frame就可以直接设置子控件的位置和尺寸 isScrollEnable:是否可以滚动.某些地方该控件是可以滚动的. titles:显示的所有标题 // MARK:- 构造函数 init(frame: CGRect, isScrollEnable : Bool, titles

  • iOS实现APP程序内部打开APP的AppStore页面

    前言 最近在做内购用到了StoreKit.framework库,偶然间发现中SKStoreProductViewController这个类,查看文档后发现一个妙用,解决了我一个痛点:APP每次更新或者跳转至某个APP的App Store页面时,都要跳转至App Store,然后再跳转回应用,用户体验很不好,以前都是直接给产品说不能解决,现在呵呵呵

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

    由于团队希望项目能够去 CoreData 化,而以往状态同步都是依赖于 CoreData 的NSFetchedResultsController.因此去 CoreData 则必须寻找一种替代方案来进行状态同步. NotificationCenter 状态同步实际是一对多的场景,也就是一个事件可以被多个观察者监听到.而苹果的系统框架自带的 NotificationCenter 正是用来适配这种场景,并且其也是被系统框架本身及我们开发者大面积使用的.用法如下: 定义通知名字,以及需要额外传递信息的

  • 详解vue父子组件状态同步的最佳方式

    哈喽!大家好!我是木瓜太香,一位老牌儿前端工程师,平时我们在使用 vue 开发的时候,可能会遇到需要父组件与子组件某个状态需要同步的情况,通常这个是因为我们封装组件的时候有一个相同的状态外面要用,里面也要用,今天我们就来看看怎么优雅的解决这个问题吧! 一般来说我们实现这个功能,只需要父组件通过 props 传递给子组件就好了,但是理想很丰满,现实很骨感,如果我们直接在子组件更改传进来的 props ,不出意外浏览器会给你一坨大红色的报错,因为在 vue 中我们的数据流动是自上而下的,而子组件直接

  • 详解iOS 实现一对多代理方案

    目录 实现方案一 实现方案二 实现方案一 利用可变数组. 签协议方需要add到代理的数组中, 然后协议遍历数组中的对象,进行分发.缺点是需要数组对其内部元素是强引用, 需要在合适的地方对其进行释放,否则会有内存泄漏 代理协议的对象.h写法 #import <UIKit/UIKit.h> NS_ASSUME_NONNULL_BEGIN @protocol TestSubViewDelegate <NSObject> - (void)testSendSomeMessageToOther

  • 详解iOS 多线程 锁 互斥 同步

    在iOS中有几种方法来解决多线程访问同一个内存地址的互斥同步问题: 方法一,@synchronized(id anObject),(最简单的方法) 会自动对参数对象加锁,保证临界区内的代码线程安全 @synchronized(self) { // 这段代码对其他 @synchronized(self) 都是互斥的 // self 指向同一个对象 } 方法二,NSLock NSLock对象实现了NSLocking protocol,包含几个方法: lock,加锁 unlock,解锁 tryLock

  • Android端内数据状态同步方案VM-Mapping详解

    目录 背景 问题拆解 目标 方案调研 EventBus 基于k-v的监听.通知 全局共享数据Model实例 基于注解的对象映射方案VM-Mapping 特点 思考 突破View层级的限制 突破类型的限制 详细设计 映射 数据驱动UI 总体流程 其它细节 方案对比 方案收益 后续计划 背景 西瓜在feed.详情页.个人主页有一块功能区,包括了点赞.收藏.关注等功能.这些功能长久以来都是孤立的:多个场景下点赞.收藏.关注等状态或数量不一致.在以往的业务迭代中,都是业务A有了需求,就加个点赞的请求,把

  • 详解IOS串行队列与并行队列进行同步或者异步的实例

    详解IOS串行队列与并行队列进行同步或者异步的实例 IOS中GCD的队列分为串行队列和并行队列,任务分为同步任务和异步任务,他们的排列组合有四种情况,下面分析这四种情况的工作方式. 同步任务,使用GCD dispatch_sync 进行派发任务 - (void)testSync { dispatch_queue_t serialQueue = dispatch_queue_create("com.zyt.queue", DISPATCH_QUEUE_SERIAL); dispatch_

  • 详解IOS判断当前网络状态的三种方法

    在项目中,为了好的用户体验,有些场景必须线判断网络状态,然后才能决定该干嘛.比如视频播放,需要线判断是Wifi还是4G,Wifi直接播放,4G先提示用户.获取网络状态的方法大概有三种: 1. Reachability 这是苹果的官方演示demo中使用到的方法,我们可以到苹果官方文档里下载Demo(点击左上角Download Sample Code 即可下载),然后把Demo里的Reachability.h和.m考到自己项目中,并在Build Phases 的 Link Binary 添加Syst

  • 详解iOS自定义UITabBar与布局

    在小编整理过的文章iOS项目基本框架搭建中,我们详细说明了如何对TabBarItem的图片属性以及文字属性进行一些自定义配置.但是,很多时候,我们需要修改TabBarItem的图片和文字属性之外,还需要自定义TabBarItem的位置,这样系统自带的TabBar的样式并不能满足我们的项目需求,所以我们需要对系统的UITabBar进行自定义,以达到我们的项目需求.例如新浪微博App的底部tab的item就无法用自带的TabBarItem进行实现,最中间那个[+]发布微博并不是用来切换tab的,而是

  • 详解Vue适时清理keepalive缓存方案

    目录 需求 思考 尝试 1. 手动操作 keep-alive 组件的 cache 数组 2. exclude 大法好 Demo 需求 单页面应用中,用户进入表单填写页面,需要初始化表单内容,填写过程中可能涉及到地图选点或者列表选择等操作,需要到新的页面选择并回调显示. 此时我们需要缓存表单填写页面实例,当退出表单填写或提交完表单内容之后,需要销毁当前表单实例,下次进入重新进行初始化 思考 说到 Vue 缓存,我们肯定首先选择官方提供的缓存方案 keep-alive 内置组件来实现. keep-a

  • 详解数据库中跨库数据表的运算

    1. 简单合并(FROM) 所谓跨库数据表,是指逻辑上同一张数据表被分别存储在不同数据库中.其原因有可能是因为数据量太大,放在一个数据库难以处理,也可能在业务上就需要将生产库和历史库分开.而不同的数据库,可能只是部署在不同的机器上的同种数据库,也可能是连类型都不同的数据库系统. 在面对跨库数据表,特别是数据库类型都不相同的情况时,数据库自带的工具往往就力所不及了,一般都需要寻找能够很好地支持多数据源类型的第三方工具,而集算器,可以说是其中的佼佼者了.下面,我们就针对几种常见的跨库混合运算情况详细

随机推荐