【问题标题】:Handling Messages from PubNub History as a "Global Variable"将 PubNub 历史中的消息作为“全局变量”处理
【发布时间】:2014-06-29 14:56:46
【问题描述】:

我正在尝试找出最聪明、最可靠的方法,让多个视图控制器可以访问我检索到的 PubNub 历史记录。正如我所看到的,有多种方法可以做到这一点,但是在阅读了一堆问题和文章之后,我无法决定哪种解决方案是最好的。实际上,我认为依赖注入在我的情况下是正确的想法,但我不确定,因为我在示例 PubNub 应用程序中看到了不同的解决方案,总的来说我从未听说过,所以如果可能的话,我会避免它。

可能的方法:

1.使用 AppDelegate

这可能是最简单的方法,但很多开发人员表示,AppDelegate 确实不是存储全局数据的最佳位置。所以我不想这样做。

2。使用单例

例如,我在 PubNub 的 iPad 演示应用程序(PNDataManager 文件)中看到了这个解决方案。我确信这是最好的方法,但是在我阅读Stephen Poletto's article on objc.io about singletons 之后,我改变了主意,因为他指出了一些问题,这对我来说可能很重要。

这是一篇非常有用的文章,值得一读,但实际上我只会抓住我认为在我的情况下重要的想法。

“假设我们正在构建一个应用程序,用户可以在其中看到他们的列表 朋友们。他们的每个朋友都有一张头像,我们想要 应用程序能够在设备上下载和缓存这些图像。使用方便的 dispatch_once sn-p,我们可能会发现自己正在编写一个 SPThumbnailCache 单例"

假设我们想要缓存message 数组而不是图像,我们在名为ViewController1 的根视图控制器中使用此方法[PubNub requestFullHistoryForChannel: withCompletionBlock:^(NSArray *message, PNChannel *channel, PNDate *fromDate, PNDate *toDate, PNError *error)}]; 检索了该数组。

“我们继续构建应用程序,一切似乎都很好, 直到有一天,当我们决定是时候实施“注销” 功能,因此用户可以在应用程序内切换帐户。突然, 我们手上有一个讨厌的问题:用户特定的状态存储在 一个全局单例。当用户退出应用程序时,我们希望成为 能够清理磁盘上的所有持久状态。否则,我们将离开 隐藏用户设备上的孤立数据,浪费宝贵的磁盘 空间。如果用户退出然后登录新帐户, 我们还希望能够为新的 SPThumbnailCache 用户。这里的问题是,根据定义,单例被假定为“创建一次,永远存在”的实例。您可以想象一些解决上述问题的方法。也许我们可以在用户退出时拆除单例实例

这里的问题是,根据定义,单例被假定为 “创建一次,永远存在”的实例。你可以想象几个 上述问题的解决方案。也许我们可以拆掉 用户退出时的单例实例。我们当然可以使这个解决方案工作,但成本太高了。一方面,我们失去了 dispatch_once 解决方案的简单性,这是一种保证线程安全的解决方案,并且所有调用 [SPThumbnailCache sharedThumbnailCache] 的代码只能获得相同的实例。我们现在需要非常小心使用缩略图缓存的代码的代码执行顺序......由于单例实例没有明确的所有者(即单例管理自己的生命周期),因此很难'关闭一个单身人士。 这里的教训是,应该只为全局状态保留单例,而不是绑定到任何范围。如果状态的范围是任何短于“我的应用程序的完整生命周期”的会话,则该状态不应由单例管理。管理用户特定状态的单例是一种代码异味,您应该重新评估您的设计对象图。"

当用户登录新帐户时,我们应该能够 构建一个全新的 SPThumbnailCache 并与之交互,没有 注意破坏旧的缩略图缓存。老人 视图控制器和旧的缩略图缓存应该被懒惰地清理 在后台自动根据典型规则 对象管理。简而言之,我们应该隔离相关的状态 来自与用户 B 关联的状态的用户 A。

我知道一个NSArrayPNMessage 对象,不包含图像是不一样的,它更容易处理它们,但我真的很困惑我应该如何处理它们。

最初我会将requestFullHistoryForChannel: withCompletionBlock: 中的message 变量传递给名为self.messageGlobal 的实例变量(我可以在每个视图中使用单例)并在用户注销时将其重置。

//inside the viewWillAppear
[PubNub requestFullHistoryForChannel: withCompletionBlock:^(NSArray *message, PNChannel *channel, PNDate *fromDate, PNDate *toDate, PNError *error)

     // self.messageGlobal should be managed by a singleton or appdelegate or use the dependency injection??
     self.messageGlobal = message;

    }];

- (IBAction)logOutButton:(id)sender {

      //disconnect from the PubNub network
      [PubNub unsubscribeFromChannel:currentUserChannel];

      //prepape ivar for the new message history
      [self.messageGlobal removeAllObjects];

      // log out user (i'm using parse.com for managing users) 
      [PFUser logOut] 
} 

在斯蒂芬的文章之后,我觉得我迷路了,如果有人能解释正确的方向,那就太棒了。

我的计划是在ViewController1 中创建一个方法来处理历史记录。

-(void)downloadPubNubHistory {

 [PubNub requestFullHistoryForChannel: withCompletionBlock:^(NSArray *message, PNChannel *channel, PNDate *fromDate, PNDate *toDate, PNError *error)

 // self.messageGlobal should be managed by a singleton or appdelegate or use the dependency injection??
 self.messageGlobal = message;

}];

}

AppDelegateViewController1 中,我会放置一个观察者以在当前用户频道收到新消息时得到通知,这很重要,因为我希望每次他或她收到新消息时都拨打downloadPubNubHistory 并且使用新内容更新当前视图。

[[PNObservationCenter defaultCenter] addMessageReceiveObserver:currentUserChannel withBlock:^(PNMessage * msg) { 

  [ViewController1 downloadPubNubHistory]; 

 } }]; 

例如,在ViewController3 中,我列出了 sampleUserB 发送到 sampleUserA(当前用户)的频道的每条消息,如果 sampleUserA's 频道在她/他的频道上收到来自 sampleUserB 的新消息,我需要重新加载 downloadPubNubHistory,还需要重新加载 ViewController3,因为我将 ViewController3 中的 self.messageGlobal 过滤为仅从sampleUserB 获取消息。

欢迎任何意见和建议,我可能误解了整个概念,我基本上可以使用单例并在每次用户注销时重置它而没有任何问题,但我想使用最轻量级的可靠解决方案。如果有任何其他推荐的技术,我也很想听听。


Original source of the article

【问题讨论】:

    标签: ios objective-c pubnub


    【解决方案1】:

    对我来说,单例始终是一种用于具体目的的工具。我没有看到多用户及其基于单例的开关的任何麻烦事件(您始终可以重置单例的状态,而不是尝试破坏它)。
    因为我们经常处理数据,我们不能在需要使用的时候一直初始化一些数据模型,因为这将花费额外的时间:分配和初始化,预设初始状态,可能从加载一些数据文件系统或网络。如果我们将在我们想要处理数据的每个视图或数据对象中执行所有这些步骤 - 它会破坏我们的应用程序响应能力并且用户会有一些糟糕的体验。
    因此,这完全取决于您将如何编写单例代码以及如何使用它。如果您有多用户应用程序并且一些敏感数据应该远离另一个用户,您可以创建一些用户实体并将所有必需的数据存储在其中(在您的情况下可以将消息传递给它)。该实体将是一些简单的数据模型或数据对象,但您仍需要有某个类负责它。在这里,您可以创建一些单例,负责管理当前用户及其数据(当您指定当前用户时,可以释放旧用户,并且可以将所有数据推送到一些本地存储中,例如 sqlite 上的 CoreData)。
    据我了解,您的聊天应用程序希望在登录后提取用户历史记录...您可以在课堂上执行此操作,该课程将管理用户实体并在当前用户更改后立即获取历史记录(在您的示例中,您每次都调用历史记录 API ,当新消息到达用户时 - 坏主意,因为历史 API 可能)。您的 PFUser 实例可以存储所有需要的数据,如应该订阅或取消订阅的频道,并且可以在 logOut 过程中使用它们。

    【讨论】:

    • 您写道,每次收到新消息时都调用历史 API 是一个坏主意,但实际上我真的不知道如何避免它。假设我们有表格视图,其中列出了用户 A 频道历史记录中的每条消息。如果用户 A 点击一个单元格(代表与用户 B 的对话),则会显示一个新视图及其消息历史记录。到此为止,一切都清楚了,在你回答之后我也完全接受了单例对象,我不再担心它了。现在我可以想象那个解决方案:userA打开应用程序,在应用程序启动时检索一次历史记录。
    • 我将历史数组传递给一个新数组(单例对象),它将保存消息。如果客户端收到一条新消息,我会将新消息放入存储历史记录的同一数组中。此后,我有一个数组,其中包含 userA 在她的频道上拥有/获取的每条消息,并且不必总是调用历史 API。当她注销时,我以某种方式消灭了单身人士,并且该应用程序已准备好迎接新的用户会话。如果用户只是关闭了应用程序,但没有注销,我只是在它再次处于活动状态时再次检索历史记录并执行相同操作。
    • 当应用程序在后台时,我将使用 APNS。通过这种方式,我避免了 CoreData 并使一切尽可能简单。您认为,这个概念在现实生活中是否可行?
    • 我对历史使用的遵守是因为我在你的addMessageReceiveObserver:withBlock: 方法使用中看到了它(基本上这意味着每次用户收到新消息时都应该提取历史——这是错误的)。您可以在需要时下载历史记录。所以如果用户选择了和另一个人聊天,你可以尝试拉本地缓存或调用历史API来获取最近的N条消息。您不必消灭单例,只需清理其中的用户数据即可。至于 APNS,我不确定您究竟是如何尝试使用它的(您的应用到达时不会处于活动状态,没有用户您也无能为力)。
    • @vv88 没错! PNObservationCenter不仅可以在AppDelegate中使用,无论是否需要获取通知都可以使用。
    猜你喜欢
    • 1970-01-01
    • 2022-10-25
    • 1970-01-01
    • 1970-01-01
    • 2020-10-16
    • 1970-01-01
    • 1970-01-01
    • 2020-04-19
    • 2021-07-22
    相关资源
    最近更新 更多