何时在 tableView 单元中分离 firebase 侦听器?

Posted

技术标签:

【中文标题】何时在 tableView 单元中分离 firebase 侦听器?【英文标题】:When to detach firebase listeners in tableView cell? 【发布时间】:2019-03-19 16:22:03 【问题描述】:

在聊天应用程序中,为了跟踪参与聊天的每个用户的每个聊天的最后一条消息和未读消息,当 tableView 单元出列时,我在单元上附加了一个 .childChanged 侦听器。当侦听器被触发时,我会更新每一行的聊天 label.text 以进行相应的聊天。 我应该什么时候删除这些听众,或者在我的情况下更新单元格中的聊天的最佳做法是什么?

程序的流程是什么? 1.下载当前用户 2. 下载当前用户聊天ID 3.为每个聊天ID下载聊天 4. 使用聊天填充 tableView 5. 在每个单元格中观察 childChanged at 聊天/chat.chatUID/currentUserUID/.observe(.childChanged) 6.如果“unreadMessagesCount”发生了变化,在单元格上更新它

class ChatTableViewCell: UITableViewCell 
  @IBOutlet weak var lastMessageLabel: UILabel!

    var chat: Chat! 
       didSet
        self.updateUI()
     
  

  func updateUI() 

    self.chat.observeChildChanged(chat: self.chat, currentUserUID:user.userUID)  (lastMessage, unreadMessagesCount) in

        if !lastMessage.isEmpty
            self.lastMessageLabel.text = lastMessage
        

        if unreadMessagesCount > 0 
            self.lastMessageLabel.font = UIFont.boldSystemFont(ofSize: 16.0)
            self.chatUnreadMessagesCount.text = "\(unreadMessagesCount)"
         else 
            self.lastMessageLabel.font = UIFont.systemFont(ofSize: 15.0)
            self.chatUnreadMessagesCount.text = ""
        
    
  
 

 class MessagesViewController: UITableViewController 
     override func viewDidLoad() 
      //observe ~/users/uid
       DDatabaseRReference.users(uid: uid).reference().observeSingleEvent(of: .value, with:  (snapshot) in

        guard snapshot.exists() else return

            if let userDict = snapshot.value as? [String : Any] 
                self.currentUser = UserModel(dictionary: userDict)
                self.userWasDownloaded = true //this will trigger the setter and start downloading chatId's of current user
            
    )
 

 var userWasDownloaded: Bool 
     get 
        return true
     
     set 
        self.fetchChatsIdsOf(currentUser: self.currentUser)
        self.tableView.reloadData()
     
 


func fetchChatsIdsOf(currentUser: UserModel) 
     //get chatIds of currentUser from ~/users/currentUser.userUID/chatIds
    DDatabaseRReference.users(uid: currentUser.userUID).reference().child("chatIds").observe(.childAdded, with:  (snapshot) in

        let chatUID = snapshot.key
        if !self.chatIdsDownloaded.contains(chatUID) 
            self.chatIdsDownloaded.append(chatUID)
        
    )


//after chatIdsDownloaded is set,
//download the new chat for the last chat appended to chatIdsDownloaded array
var chatIdsDownloaded = [String]() 
    didSet 
        guard let chatID = chatIdsDownloaded.last else  return
        self.downloadNewChat(chatID: chatID)
    


    func downloadNewChat(chatID: String) 
   DDatabaseRReference.chats.reference().child(chatID).observeSingleEvent(of: .value, with:  (snapshot) in
        ......
      self.currentUserChats.insert(chatChecked, at: 0)
     
   

    override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell 

    let cell = tableView.dequeueReusableCell(withIdentifier: "ChatTableViewCell", for: indexPath) as! ChatTableViewCell
       cell.chat = currentUserChats[indexPath.row]
      return cell
    
  



chats    // <- all chats in the app for all users in the app
 -LOMVtcjOEOu2p1apMKV
   chatUID: "-LOMVtcjOEOu2p1apMKV"
   isGroupChat: true
   lastMessage: "Katherine Gregory has joined the group"
   lastUpdate: 1539761870.2237191
   +users
     IN4pgCS5NqQZZLpdmoz1KeDiFqj2
     fcmToken: ""
     firstName: "Alex"
     userUID: "IN4pgCS5NqQZZLpdmoz1KeDiFqj2"
     unreadMessagesCount: 5



users   // <- all users in the app
 IN4pgCS5NqQZZLpdmoz1KeDiFqj2
   +chatIds
      -LOMVtcjOEOu2p1apMKV: true
      - name: ""
      - email: ""
       ...etc

【问题讨论】:

我不建议在单元格中使用侦听器或那种逻辑 - 这可能会使您的 UI 滞后。 IMO tableViews 是由数据源支持的 UI 元素。当数据发生变化时,更新数据源,然后重新加载 tableView,然后更新 UI。您的 viewController 会将 .childAdded、.childChanged 和 .childRemoved 观察者添加到 Firebase。当发生更改时,您的类会收到通知,您可以相应地更新 dataSource 数组。我建议使用一个名为 is_read 的子节点,它在未读取时为 false,因此您可以轻松跟踪已读/未读消息。 当您开始收听.onSnapshot 时,您会下载现有的内容,然后当且仅当它(收听的路径)发生更改时,您才会获得更新。因此,无需“下载”。只需 .onSnapshot 您想要传递给您的 UI/应用程序的东西。 @Jay 我知道数据源应该支持 UI,但我不知道从哪里开始。我确实在users/currentUserUID 上附加了.childAdded、.childChanged、.childRemoved,这样我就可以读取/删除/写入currentUser 的chatUID。但是,一旦我阅读了 currentUser 的 chatUID,我需要下载存储在chats/chatUID 的聊天记录。现在,问题是我需要在每次下载的聊天中观察 .childChanged。 unreadMessagesCount: 5chats/chatUID/users/currentUserUID/ unreadMessagesCount: 5. 的孩子 我们不知道结构,但应该是向 viewController 中的每个聊天(而不是每个单元格)添加 .childAdded 观察者。这将在每个聊天中读取,然后通知您任何新添加的聊天。我会首先让您的 tableView 从 dataSource 数组中读取它的数据,在 viewController 中有一个处理 .childAdded 事件的函数,该事件会在初始化时使用现有的聊天信息相应地更新您的 dataSource,并在这些事件发生时添加新的聊天信息。例如,聊天 3 有新聊天,事件将触发,更新聊天 3 的 dataSource 数组,重新加载 tableView。 我明白了,我会开始工作的。谢谢 【参考方案1】:

您可以检查是否为单元添加了 2 个或更多观察者。

在此处添加断点或 print():

self.chat.observeChildChanged(chat: self.chat, currentUserUID: user.userUID)  (lastMessage, unreadMessagesCount) in 
   //breakpoint or print("observeChildChanged")
   ...

请重复使用您的手机。 发送新消息。

如果您有 2 条或更多消息,则表示您没有只设置一个观察者。

也许这种方法并不完美,但它可以帮助你(在添加新的之前删除旧的观察者):

var chat: Chat! 
   didSet 
      self.removeOldObserver()
      self.updateUI()
   


func removeOldObserver() 
   ...

【讨论】:

【参考方案2】:

正如 Jay 所建议的,我在新下载的每个聊天中都附上了 .childChanged 观察者。 但是,如果我转到 firebase 控制台并更新 ref 路径上的孩子的值,则不会总是触发 childChanged 观察者。有时有效,有时无效 可能是什么问题? 我在所有行上都使用了断点,当数据库中的值发生变化时,它们都没有被命中。

示例:name:"Alex" 更改为:名称:“约翰”

更新 这个答案是正确的,我忘记删除我之前在prepareForReuse()中分离监听器的实现@

var currentUserChats = [Chat]() 
    didSet(newValue)
       attachChildChangedObserverOn(chat: newValue)
    


var observersArray = [String: UInt]() // chatUID:handle


  func attachChildChangedObserverOn(chat: Chat) 

     var handle: UInt = 0
     let ref =   DDatabaseRReference.chats.reference().child(chat.chatUID).child("users").child(currentUser.userUID)
    handle = ref.observe(.childChanged, with: [weak self] (snapshot) in

     self?.observersArray[chat.chatUID] = handle

        print("snapshot.value is \(snapshot.value) and snapKey is \(snapshot.key)")
        guard snapshot.exists() else return

        let chatChanged = chat

        var lastMessage = ""
        var unreadMessagesCount = 0
        var lastUpdate = 0.0

        switch snapshot.key 
        //case....
       

     )

  

【讨论】:

以上是关于何时在 tableView 单元中分离 firebase 侦听器?的主要内容,如果未能解决你的问题,请参考以下文章

std::thread 线程在对象中分离,它何时终止?

如何从 ViewController 中分离(推送)到 NavigationController 中嵌入的另一个 ViewController?

将 UITableView 委托和数据源从 tableViewController 中分离出来

在 Go 中分离单元测试和集成测试

如何使用注释在 testng + maven 中分离单元测试和集成测试?

通过点击单元格内的图像从 UITableViewCell 中分离