【发布时间】:2018-02-18 10:33:43
【问题描述】:
我正在监视来自多个UIViewControllers 的一些数据的状态。当UIViewController deinit 发生时,我使用句柄删除特定的观察者。现在我注意到,在从一个 UIViewController 中删除查询观察者之后,将来另一个 UIViewController 上的查询观察者不会被调用,尽管数据正在改变。请告诉我如何解决?
open func observeUserFriendshipEnd(_ observer: FirebaseObserverDelegate, isObserve: Bool, friendID: String, endHandler: ((_ removedFriendModel: FriendModel) -> Void)?, fail: ((_ error: Error) -> Void)?) {
let observerID = observer.observerID
let realmManager = RealmManager()
guard let currentUserID = realmManager.getCurrentUser()?.id else { return }
let query = Database.database().reference().child(FriendsPaths.MainGateways.friends.description).child(currentUserID).child(FriendsPaths.SubGateways.userFriends.description).queryOrdered(byChild: "friendID").queryEqual(toValue: friendID)
if !isObserve {
guard let handle = self.userFriendshipEndObservers[observerID] else { return }
query.removeObserver(withHandle: handle)
self.userFriendshipEndObservers[observerID] = nil
// system
removeObserverModel(observerID, handle: handle)
return
}
DispatchQueue.global(qos: .background).async {
var isContinue = true
self.queue.sync {
if self.userFriendshipEndObservers[observerID] != nil {
isContinue = false
}
}
guard isContinue else { return }
var handle: UInt = 0
handle = query.observe(.childRemoved, with: { (snap) in
if snap.value is NSNull {
return
}
guard let dict = snap.value as? [String : Any] else { return }
guard let removedFriendModel = Mapper<FriendModel>().map(JSON: dict) else { return }
let friendRealmManager = FriendRealmManager()
friendRealmManager.removeFriend(removedFriendModel.friendID)
if removedFriendModel.friendID == friendID {
endHandler?(removedFriendModel)
}
}, withCancel: { (error) in
fail?(error)
})
self.queue.sync {
self.userFriendshipEndObservers[observerID] = handle
self.addObserver(observerID, handle: handle, query: query, ref: nil)
}
}
}
更新:我输出到日志,句柄,一切都被正确添加和删除。比如 ProfileViewController 有 33 个句柄,ChatViewController 有 85 个句柄,在 ChatViewController deinit 后,删除了 85 个句柄的观察者,原来有 33 个句柄的观察者,但它没有被调用。
更新 1 如何知道:- 有多少观察者有 firebase 参考?
更新 2
class ProfileUserFormVC: FormViewController {
// MARK: - Lifecycle
override func viewDidLoad() {
super.viewDidLoad()
addObservers()
}
deinit {
removeObservers()
NotificationCenter.default.removeObserver(self)
}
}
// MARK: - Obsevers
extension ProfileUserFormVC {
private func addObservers() {
guard let userID = self.userID else { return }
MatchObserverManager.shared.observeNewMatchWithUser(self, isObserve: true, userID: userID, completion: { [weak self] (match) in
// do something
}) { (error) in
}
MatchObserverManager.shared.observeUserMatchDeleting(self, isObserve: true, userID: userID, completion: { [weak self] (removedMatch) in
// do something
}, fail: nil)
FriendsObserver.shared.observeUserFriendshipEnd(self, isObserve: true, friendID: userID, endHandler: { [weak self] (removedFriend) in
DispatchQueue.main.async {
// do something with ui
}
}, fail: nil)
}
private func removeObservers() {
guard let userID = user?.id else { return }
MatchObserverManager.shared.observeNewMatchWithUser(self, isObserve: false, userID: userID, completion: nil, fail: nil)
MatchObserverManager.shared.observeUserMatchDeleting(self, isObserve: false, userID: userID, completion: nil, fail: nil)
FriendsObserver.shared.observeUserFriendshipEnd(self, isObserve: false, friendID: userID, endHandler: nil, fail: nil)
}
}
经理
class FriendsObserver: FirebaseObserver {
static let shared = FriendsObserver()
private override init() {
super.init()
}
// MARK: - Queues
private let queue = DispatchQueue(label: "com.myapp.FriendsObserver.queue")
// MARK: - Data
private var userFriendshipEndObservers = [String : UInt]()
open func observeUserFriendshipEnd(_ observer: FirebaseObserverDelegate, isObserve: Bool, friendID: String, endHandler: ((_ removedFriendModel: FriendModel) -> Void)?, fail: ((_ error: Error) -> Void)?) {
let observerID = observer.observerID
let realmManager = RealmManager()
guard let currentUserID = realmManager.getCurrentUser()?.id else { return }
let query = Database.database().reference().child(FriendsPaths.MainGateways.friends.description).child(currentUserID).child(FriendsPaths.SubGateways.userFriends.description).queryOrdered(byChild: "friendID").queryEqual(toValue: friendID)
if !isObserve {
guard let handle = self.userFriendshipEndObservers[observerID] else { return }
query.removeObserver(withHandle: handle)
self.userFriendshipEndObservers[observerID] = nil
// system
removeObserverModel(observerID, handle: handle)
return
}
DispatchQueue.global(qos: .background).async {
var isContinue = true
self.queue.sync {
if self.userFriendshipEndObservers[observerID] != nil {
isContinue = false
}
}
guard isContinue else { return }
var handle: UInt = 0
handle = query.observe(.childRemoved, with: { (snap) in
if snap.value is NSNull {
return
}
guard let dict = snap.value as? [String : Any] else { return }
guard let removedFriendModel = Mapper<FriendModel>().map(JSON: dict) else { return }
let friendRealmManager = FriendRealmManager()
friendRealmManager.removeFriend(removedFriendModel.friendID)
if removedFriendModel.friendID == friendID {
endHandler?(removedFriendModel)
}
}, withCancel: { (error) in
fail?(error)
})
self.queue.sync {
self.userFriendshipEndObservers[observerID] = handle
self.addObserver(observerID, handle: handle, query: query, ref: nil)
}
}
}
}
Update 3我做了一个快速的更改,虽然这样做是不对的,但是我在收到句柄后将句柄返回给了handler,然后我为ViewController分配了一个变量,并且在deinit 我也删除了观察者。我得到了同样的结果。也就是说,如果我删除了一个带有句柄的观察者,那么第二个就不起作用了。在此之前,我的代码以原始形式运行。
open func observeUserFriendshipEnd(_ observer: FirebaseObserverDelegate, isObserve: Bool, friendID: String, handle: UInt?, handleHandler: ((_ handle: UInt) -> Void)?, endHandler: ((_ removedFriendModel: FriendModel) -> Void)?, fail: ((_ error: Error) -> Void)?) {
let observerID = observer.observerID
let realmManager = RealmManager()
guard let currentUserID = realmManager.getCurrentUser()?.id else { return }
let query = Database.database().reference().child(FriendsPaths.MainGateways.friends.description).child(currentUserID).child(FriendsPaths.SubGateways.userFriends.description).queryOrdered(byChild: "friendID").queryEqual(toValue: friendID)
if !isObserve {
guard let _handle = handle else { return }
query.removeObserver(withHandle: _handle)
return
}
DispatchQueue.global(qos: .background).async {
var isContinue = true
self.queue.sync {
if self.userFriendshipEndObservers[observerID] != nil {
isContinue = false
}
}
guard isContinue else { return }
var handle: UInt = 0
handle = query.observe(.childRemoved, with: { (snap) in
if snap.value is NSNull {
return
}
guard let dict = snap.value as? [String : Any] else { return }
guard let removedFriendModel = Mapper<FriendModel>().map(JSON: dict) else { return }
let friendRealmManager = FriendRealmManager()
friendRealmManager.removeFriend(removedFriendModel.friendID)
if removedFriendModel.friendID == friendID {
endHandler?(removedFriendModel)
}
}, withCancel: { (error) in
fail?(error)
})
handleHandler?(handle)
self.queue.sync {
self.userFriendshipEndObservers[observerID] = handle
self.addObserver(observerID, handle: handle, query: query, ref: nil)
}
}
}
【问题讨论】:
-
如果你有 85 个观察者,你可能做错了。但是,您的问题中包含的代码和用例的描述并不能真正表明正在发生的事情。其次,仅仅因为您添加了 5 个观察者,然后删除了 5 个观察者,并不意味着添加或删除了 正确的 个观察者。即当 ViewController 超出范围时,您可能会删除错误的观察者。您的代码中有很多外部调用也可能造成问题。请查看How to create a Minimal, Complete, and Verifiable example
-
@Jay 对不起,你误会了,我没有85个观察者,但这是把手图85,只有两个观察者,现在我会写一个更完整的例子。跨度>
-
@Jay 请查看更新 2;此外,我将观察者添加到 ChatViewController,当 ChatViewController deinit 时,我打开配置文件,在数据删除后观察者处理程序应该工作,但它不起作用,尽管我输出到日志,观察者是正确的。跨度>
-
明白了。但是,我的评论仍然适用。句柄似乎失控了,可能无法以始终如一的方式实现——我们不知道外部调用做了什么。我建议将句柄存储在全局数组中(例如)并根据需要添加和删除。它使其井井有条,并在一个地方。请参阅我的示例here,了解另一种保持紧缩的策略。
-
乍一看,你有很多不必要的代码。如果您将观察者添加到视图控制器中的节点,请将句柄存储为视图控制器的类 var。当控制器关闭时,使用该 var(句柄)删除观察者。实际上,您有存储对句柄的引用的类,我敢打赌,由于代码的原因,您的句柄在错误的时间被覆盖和/或删除;即 ViewController1 关闭但意外存储了 ViewController2 的句柄,因此当它关闭时,ViewController2 的观察者被删除。有很多代码可以真正说明。
标签: ios swift firebase firebase-realtime-database firebase-queue