Firebase removeAllObservers() продолжает совершать вызовы при переключении представлений -Swift2 iOS

Я читал другие вопросы и ответы о переполнении стека об этой проблеме, но, похоже, это проблема tabBarController, о которой я ничего не нашел.

У меня есть tabBarController с 3 вкладками. tab1 — это место, где я успешно отправляю информацию в Firebase. В tab2 у меня есть tableView, который успешно читает данные из tab1.

В tab2 мой слушатель находится в поле зренияWillAppear. Я удаляю прослушиватель в viewDidDisappear (я также пробовал viewWillDisappear), и где-то здесь возникает проблема.

override func viewDidDisappear(animated: Bool) {
    self.sneakersRef!.removeAllObservers()
    //When I tried using a handle in viewWillAppear and then using the handle to remove the observer it didn't work here either
    //sneakersRef!.removeObserverWithHandle(self.handle)
   }

Как только я переключаюсь с tab2 на любую другую вкладку, во второй раз я возвращаюсь к tab2, данные таблицы удваиваются, затем, если я снова переключаю вкладки и возвращаюсь, они утраиваются и т. д. Я попытался установить прослушиватель внутри viewDidLoad, который предотвращает дублирование данных таблицы, когда Я переключаю вкладки, но когда я отправляю новую информацию с вкладки 1, информация никогда не обновляется на вкладке 2.

Согласно документам Firebase и почти всему остальному, что я читал в stackoverflow/google, прослушиватель должен быть установлен в viewWillAppear и удален в viewDidDisappear.

Любые идеи о том, как предотвратить дублирование моих данных всякий раз, когда я переключаюсь между вкладками?

вкладка1

import UIKit
import Firebase
import FirebaseAuth
import FirebaseDatabase

class TabOneController: UIViewController{

var ref:FIRDatabaseReference!
@IBOutlet weak var sneakerNameTextField: UITextField!

override func viewDidLoad() {
      super.viewDidLoad()
      self.ref = FIRDatabase.database().reference()
}

@IBAction func sendButton(sender: UIButton) {

      let dict = ["sneakerName":self.sneakerNameTextField.text!]

      let usersRef = self.ref.child("users")
      let sneakersRef = usersRef.child("sneakers").childByAutoID()

      sneakersRef?.updateChildValues(dict, withCompletionBlock: {(error, user) in
                if error != nil{
                    print("\n\(error?.localizedDescription)")
                }
            })
      }
}

вкладка2

import UIKit
import Firebase
import FirebaseAuth
import FirebaseDatabase

class TabTwoController: UITableViewController{

@IBOutlet weak var tableView: UITableView!

var handle: Uint!//If you read the comments I tried using this but nahhh didn't help
var ref: FIRDatabaseReference!
var sneakersRef: FIRDatabaseReference?
var sneakerArray = [String]()

override func viewDidLoad() {
      super.viewDidLoad()
      self.ref = FIRDatabase.database().reference()
    }

override func viewWillAppear(animated: Bool) {
      super.viewWillAppear(animated)

      let usersRef = self.ref.child("users")
      //Listener
      self.sneakersRef = usersRef.child("sneakers")

      //I tried using the handle
      //---self.handle = self.sneakersRef!.observeEventType(.ChildAdded...

      self.sneakersRef!.observeEventType(.ChildAdded, withBlock: {(snapshot) in

      if let dict = snapshot.value as? [String:AnyObject]{

        let sneakerName = dict["sneakerName"] as? String
        self.sneakerArray.append(sneakerName)

        dispatch_async(dispatch_get_main_queue(), {
                self.tableView.reloadData()
       })

     }

   }), withCancelBlock: nil)
 }

//I also tried viewWillDisappear
override func viewDidDisappear(animated: Bool) {

        self.sneakersRef!.removeAllObservers()

        //When I tried using the handle to remove the observer it didn't work either
        //---sneakersRef!.removeObserverWithHandle(self.handle)
   }


func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
      return self.sneakerArray.count
  }

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
      let cell = self.tableView.dequeueReusableCellWithIdentifier("sneakerCell", forIndexPath: indexPath)
      cell.textLabel?.text = sneakerArray[indexPath.row]
      return cell
  }
}

таб3 ничего не делает. Я просто использую его как альтернативную вкладку для переключения туда и обратно с помощью


person Lance Samaria    schedule 27.09.2016    source источник


Ответы (3)


Метод removeAllObservers работает правильно. Проблема, с которой вы столкнулись в настоящее время, вызвана отсутствующей незначительной деталью: вы никогда не очистите содержимое файла sneakerArray.


Примечание: нет необходимости в dispatch_async внутри обратного вызова события. Firebase SDK вызывает эту функцию в основной очереди.

person vzsg    schedule 30.09.2016
comment
Спасибо за вашу помощь. Я удалил содержимое массива в viewDidDisappear после метода removeAllObserver() следующим образом: self.sneakersRef!.removeAllObservers(), а затем self.sneakerArray.removeAll(). Он перестал дублироваться. Я был действительно напряжен по этому поводу. Можете ли вы объяснить, почему мне нужно было очистить массив? Насколько я понимаю, каждый раз, когда представление загружается снова, содержимое массива остается одинаковым. - person Lance Samaria; 30.09.2016
comment
Когда вы начинаете подписку на узел Firebase, первое, что делает SDK, — загружает текущее содержимое. С событиями .childAdded это означает, что вы получаете один обратный вызов для каждого дочернего элемента; каждый раз, когда вы подписываетесь. Вы просто добавляли потомков к каждому обратному вызову, что приводило к дублированию. - person vzsg; 30.09.2016

Официально принятый ответ был опубликован @vzsg. Я просто подробно описываю это для следующего человека, который столкнется с этой проблемой. Голосуйте за его ответ.

Что в основном происходит, так это то, что когда вы используете .childAdded, Firebase зацикливается и захватывает каждого дочернего элемента из узла (in my case the node is "sneakersRef"). Firebase захватывает данные, добавляет их в массив (in my case self.sneakerArray), затем возвращается, чтобы получить следующий набор данных, добавляет их в массив и т. д. При очистке массива в начале следующего цикла массив будет пустым. как только FB будет готов. Если вы переключаете сцены, у вас всегда будет пустой массив, и единственные данные, которые будет отображать пользовательский интерфейс, — это FB, снова и снова зацикливающий узел.

Также я избавился от вызова dispatch_async, потому что он сказал, что FB вызывает функцию в основной очереди. Я использовал только self.tableView.reloadData()

Кстати, вы должны подписаться на firebase-community.slack.com. Это официальный слабый канал Firebase, где я разместил этот вопрос, и vzsg ответил на него.

override func viewWillAppear(animated: Bool) {
      super.viewWillAppear(animated)

  //CLEAR THE ARRAY DATA HERE before you make your Firebase query
  self.sneakerArray.removeAll()

  let usersRef = self.ref.child("users")
  //Listener
  self.sneakersRef = usersRef.child("sneakers")

  //I tried using the handle
  //---self.handle = self.sneakersRef!.observeEventType(.ChildAdded...

  self.sneakersRef!.observeEventType(.ChildAdded, withBlock: {(snapshot) in

  if let dict = snapshot.value as? [String:AnyObject]{

    let sneakerName = dict["sneakerName"] as? String
    self.sneakerArray.append(sneakerName)

    //I didn't have to use the dispatch_async here
    self.tableView.reloadData()

  }
 }), withCancelBlock: nil)
}
person Lance Samaria    schedule 30.09.2016

Спасибо, что задали этот вопрос.

Я также сталкивался с той же проблемой в прошлом. Я решил эту проблему следующим образом.

1) Определите массив, содержащий список наблюдателей.

var aryObserver = Array<Firebase>()

2) Добавьте наблюдателя следующим образом.

 self.sneakersRef = usersRef.child("sneakers")
 aryObserver.append( self.sneakersRef)

3) Удалите наблюдателя следующим образом.

  for fireBaseRef in self.aryObserver{
            fireBaseRef.removeAllObservers()
        }

 self.aryObserver.removeAll() //Remove all object from array.
person Hitesh Surani    schedule 27.09.2016
comment
Поместите последний код в viewWillDisappear вместо viewDidDisappear, потому что сначала его вызов. - person Hitesh Surani; 27.09.2016
comment
До сих пор не работает. Я попытался переместить код в несколько разных мест. Надааааа - person Lance Samaria; 27.09.2016
comment
не могли бы вы поделиться со мной демо-проектом - person Hitesh Surani; 27.09.2016
comment
Это проект выше. Вы можете продублировать его. Он должен работать точно так же. Попробуй это. - person Lance Samaria; 27.09.2016