使用监听器时内存泄漏

问题描述 投票:0回答:0

我有一个应用程序可以从 Firestore 获取和收听群聊。

我在我的 fetchGroups 函数中添加了 3 个侦听器,以根据组是否已保存或组所有者是否已阻止/已被当前用户阻止来过滤组。

过滤机制有效,但一旦加载视图并获取组就会导致内存泄漏。

仅当使用一个或多个侦听器进行过滤时才会发生泄漏,这意味着,例如,如果我删除

!message.isSaved
过滤器并保留剩余的2个过滤器并且没有人阻止我+我没有阻止任何人,我没有得到泄漏,否则我做...

正如预期的那样,当我卸下所有过滤器时

if !message.isSaved || message.hasBlocked || message.isBlocked
我一点也没有泄漏。

这是我使用 Instrument 时的泄漏结果:

这是我的代码:

群聊VC

 private var lastDocumentSnapshot: DocumentSnapshot?
    
 private var listener: [ListenerRegistration] = []
    
 private var isListening = true
    
 private var theyBlockedU: [String] = []
 private var uBlockedThem: [String] = []
 
 private var groups = [Group]()

    override func viewDidLoad() {
        super.viewDidLoad()
              fetchGroups()
           }
    override func viewWillAppear(_ animated: Bool) {
        self.isListening = true
    }
    override func viewDidDisappear(_ animated: Bool) {
       self.isListening = false
     }
      // MARK: - API
    func fetchGroups() {
            self.fetchGroups() { [weak self] groups in
                guard let self = self else{return}
                groups.forEach { group in
                    self.groupsDictionary[group.recentMessage.groupID] = group
                }
                self.groups = Array(self.groupsDictionary.values)
                self.groups.sort(by: { $0.recentMessage.timestamp.compare($1.recentMessage.timestamp) == .orderedDescending })
                self.tableView.reloadData()
            }
        self.tableView.refreshControl?.endRefreshing()
    }

      func fetchGroups(completion: @escaping([Group]) -> Void) {
        var query: Query!
        tableView.refreshControl?.beginRefreshing()

         if groups.isEmpty {
               query = COLLECTION_GROUPS.order(by: "timestamp", descending: false)
           } else {
               if let lastDocSnapshot = lastDocumentSnapshot {
                   query = COLLECTION_GROUPS.order(by: "timestamp", descending: false).end(beforeDocument: lastDocSnapshot)
               } else {
                   print("Error: lastDocumentSnapshot is nil")
                   return
               }
           }
         
        let listener = query.addSnapshotListener { [weak self] (snapshot, err) in
            guard let self = self else{return}
            if let err = err {
                print("\(err.localizedDescription)")
            } else if snapshot!.isEmpty {
                self.isLoading = false
                self.stopActivityIndicator()
                self.tableView.refreshControl?.endRefreshing()
                return
            }
            guard let lastSnap = snapshot?.documents.first else {return}
            self.lastDocumentSnapshot = lastSnap
            
            snapshot?.documentChanges.forEach({ (change) in
                 let dictionary = change.document.data()
                 var message = GroupMessage(dictionary: dictionary)
                  
                 UserService.fetchUser(withUid: message.senderID) { [weak self] user in
               guard let self = self else { return }
               
    self.isUserBlocked(ownerID: message.ownerID) { [weak self] isBlocked in
              guard let self = self else { return }
              message.isBlocked = isBlocked

            self.isBlockedByUser(ownerID: message.ownerID) { [weak self] hasBlocked in
                   guard let self = self else { return }
                   message.hasBlocked = hasBlocked

               self.listenForGroupSaved(groupID: message.groupID) { [weak self] isSaved in
                           guard let self = self else { return }
                           message.isSaved = isSaved
                               
                               let group = Group(user: user, recentMessage: message)
                   
                                    if  !message.isSaved || message.hasBlocked || message.isBlocked  { // If one of those is used, i get leaks...
                                          self.groups = self.groups.filter { $0.recentMessage.groupID != message.groupID }
                                          self.tableView.reloadData()
                                       return
                                       } else {
                                       if let index = self.groups.firstIndex(where: { $0.recentMessage.groupID == message.groupID }) {
                                        self.groups[index] = group
                                        } else {
                                        self.groups.append(group)
                                        self.groups.sort(by: { $0.recentMessage.timestamp.compare($1.recentMessage.timestamp) == .orderedDescending })
                                        }
                                       self.groups.sort(by: { $0.recentMessage.timestamp.compare($1.recentMessage.timestamp) == .orderedDescending })
                                       self.tableView.reloadData()
                                   }
                      }
                  }
              }
         }
         })
                self.tableView.refreshControl?.endRefreshing()
                self.lastDocumentSnapshot = snapshot?.documents.first
        }
        if isListening == false { listener.remove()}
      }
       func listenForGroupSaved(groupID: String, completion: @escaping (Bool) -> Void) {
         guard let currentUser = Auth.auth().currentUser else {return}
         let docRef = COLLECTION_USERS.document(currentUser.uid).collection("isGroupSaved").document(groupID)
        let listener = docRef.addSnapshotListener { (snapshot, error) in
             if let error = error {
                 print("Error listening for savedGroup changes: \(error.localizedDescription)")
                 return
             }
             if let snapshot = snapshot, snapshot.exists {
                guard let isSaved = snapshot.data()?["isSaved"] as? Bool else{return}
                 completion(isSaved)
             } else {
             // Provide a default value for defaultIsSaved if no document is found
                    let defaultIsSaved = false
                       completion(defaultIsSaved)
             }
         }
        if isListening == false { listener.remove()}
      }
    func isUserBlocked(ownerID: String, completion: @escaping(Bool) -> Void) {
        guard let currentUser = Auth.auth().currentUser else { return }
        let listener = COLLECTION_USERS.document(currentUser.uid).collection("blocked").addSnapshotListener { [weak self] (querySnapshot, err) in
            guard let self = self else { return }
            if let err = err {
                print("Error getting documents: \(err)")
            } else {
                self.uBlockedThem = querySnapshot!.documents.map({ $0.documentID })
                let isBlocked = self.uBlockedThem.contains(ownerID)
                completion(isBlocked)
            }
        }
        if isListening == false { listener.remove()}
    }
    func isBlockedByUser(ownerID: String, completion: @escaping(Bool) -> Void) {
        guard let currentUser = Auth.auth().currentUser else {return}
        let listener = COLLECTION_USERS.document(currentUser.uid).collection("isBlockedBy").addSnapshotListener { [weak self] (querySnapshot, err) in
            guard let self = self else { return }
            if let err = err {
                print("Error getting documents: \(err)")
            } else {
                self.theyBlockedU = querySnapshot!.documents.map({ $0.documentID })
                let isBlocked = self.theyBlockedU.contains(ownerID)
                completion(isBlocked)
            }
        }
        if isListening == false { listener.remove()}
    }

集团结构

  struct Group {
    var user: User
    var recentMessage: GroupMessage
}
struct GroupMessage {
    var user: User?
    
   var text: String
   var senderID: String

    var groupID : String
    var ownerID: String

    var timestamp: Timestamp

    var isSaved: Bool
    var isBlocked: Bool
    var hasBlocked: Bool
    
    init(dictionary: [String: Any]) {
        
        self.text = dictionary["text"] as? String ?? ""
        self.senderID = dictionary["senderID"] as? String ?? ""

        self.groupID = dictionary["groupID"] as? String ?? ""
        self.ownerID = dictionary["ownerID"] as? String ?? ""
        
        self.timestamp = dictionary["timestamp"] as? Timestamp ?? Timestamp(date: Date())
        
        self.isSaved = dictionary["isSaved"] as? Bool ?? false
        self.isBlocked = dictionary["isBlocked"] as? Bool ?? false
        self.hasBlocked = dictionary["hasBlocked"] as? Bool ?? false

    }
}


  
swift google-cloud-firestore tableview listener
© www.soinside.com 2019 - 2024. All rights reserved.