0

I am facing a really mind-breaking problem..

So my application was working just fine, until now..


The app has a TabController: with 5 viewControllers

On this TabController I have a timer that fetches data every 10 seconds

This data is being sent with notifications to one of its child VCs called "WorldMessages", which then updates it's tableView

I don't know how or why, but whenever I log out / log in in my application, the WorldMessages ViewController instance get stuck..

So for example after 5 relogin, I have 5 WorldMessages VC in the memory..

// Relogin means that the TabController and its childViews are getting destroyed, and then logging in back creates a new instance of TabController //


I have a clue that it is because of the threading, but I am not sure. When I delete a few lines, it is working okay. Can anyone help me out?

( If i delete those lines in the WorldMessages viewController:

tableView.beginUpdates()
tableView.deleteRows(at: tableViewDeletes, with: .fade)
tableView.insertRows(at: tableViewInserts, with: .fade)
tableView.endUpdates()

Then the app is working fine.. )

TabController, the function that gets called every 10 seconds:

@objc func fetchWorldMessages(scrollToTop: Bool){

        worldMessagesFetch.fetchWorldMessages() { response, worldMessageData in
            if let response = response {
                if response.type == 1 {
                    // Fetched data
                    if let worldMessageData = worldMessageData {
                        DispatchQueue.main.async {
                        NotificationCenter.default.post(name: .updateWorldMessages, object: nil, userInfo: worldMessageData)
                        }
                    }
                } else {
                    // Can not fetch data
                    self.handleResponses.displayError(title: response.title, message: response.message)

                    WorldMessagesStore.shared.clear()
                    NotificationCenter.default.post(name: .reloadWorldMessagesTableView, object: nil)
                }
            }
        }
    }

The WorldMessages viewController:

@objc func notification_updateWorldMessages(notification: NSNotification){

        self.refreshControl.endRefreshing()

        if let newWorldMessages = notification.userInfo?["newWorldMessages"] as? [WorldMessage], let newDeleteArray = notification.userInfo?["newDeleteArray"] as? [Int], let newAppendArray = notification.userInfo?["newAppendArray"] as? [Int], let newWorldMessagesCount = notification.userInfo? ["newWorldMessagesCount"] as? Int, let worldMessagesCount = notification.userInfo? ["worldMessagesCount"] as? Int {

            if (newWorldMessagesCount == 0 && noWorldMessages.count == 0){
                noWorldMessages = [1]
                tableView.reloadSections(IndexSet(integersIn: 1...1), with: .automatic)
            } else if (newWorldMessagesCount != 0 && noWorldMessages.count != 0) {
                noWorldMessages = []
                tableView.reloadSections(IndexSet(integersIn: 1...1), with: .automatic)
            }

            var count = 0
            count = newWorldMessagesCount


            if count != 0 {

                var tableViewDeletes : [IndexPath] = []
                for i in stride(from: count - 1, to: -1, by: -1) {
                    // WHAT SHOULD BE DELETED
                    WorldMessagesStore.shared.worldMessages.remove(at: i)
                }

                var tableViewInserts : [IndexPath] = []
                for i in stride(from: count - 1, to: -1, by: -1) {
                    // WHAT SHOULD BE ADDED
                    WorldMessagesStore.shared.worldMessages.insert(newWorldMessages[i], at: 0)

                }

                tableView.beginUpdates()
                tableView.deleteRows(at: tableViewDeletes, with: .fade)
                tableView.insertRows(at: tableViewInserts, with: .fade)
                tableView.endUpdates()
            }

        }

    }

Image:

(In my app "WorldMessages VC" is called Main VC)

So after 2 relogin It get stuck 2 times: (You see 2 instances)

enter image description here

VIDEO of the bug: (demonstrating that if I delete those 4 lines everything works fine)

https://www.youtube.com/watch?v=7xfTzbZplBA

Kárpáti András
  • 1,221
  • 1
  • 16
  • 35
  • 1
    What you mean by `I have 5 WorldMessages VC in the memory` ? – Abdelahad Darwish May 21 '18 at 01:09
  • 1
    what do you do when relogin – Abdelahad Darwish May 21 '18 at 01:16
  • @AbdelahadDarwish updated – Kárpáti András May 21 '18 at 01:26
  • What do you do to signup are you present signup controller or set as root – Abdelahad Darwish May 21 '18 at 01:47
  • 1
    I'm assuming that `WorldMessages` is either a subclass of `UITableViewController` or at least conforms to `UITableViewDelegate` and `UITableViewDataSource`. If you remove the lines that update the table view, then those protocol methods won't be called. Therefore, I suspect those methods are creating a retain cycle. The most likely culprit would be the methods for `UITableViewDataSource`, specifically `tableView(_:cellForRowAt:)`. – ABeard89 May 21 '18 at 04:29
  • 1
    The memory graph in your video also proves this (around 0:35). The cell objects are pointing to your view controller. This means that they have a reference to your view controller. And the view controller also has references to them (indirectly, because they are children of the table view). It definitely looks like you're creating a retain cycle between the view controller, the table view, and the table view cells. – ABeard89 May 21 '18 at 04:32
  • @ABeard89 adding a new reply – Kárpáti András May 21 '18 at 08:53

1 Answers1

1

After hours of trying and re-reading the replies I got, I realized something.

I have gestureRecognizers on my custom cells, therefore I had to add a delegate to them: and (for now, It seems) this caused the problem

import UIKit
import SwipeCellKit

class WorldMessageCell: SwipeTableViewCell {
    var worldMessageData : WorldMessage!

    @IBOutlet var bubbleView: UIView!
    @IBOutlet var bubbleButton: UIButton!

    override var canBecomeFirstResponder: Bool {
        return true
    }

    var delegate2: WorldMessageDelegate?

    override func awakeFromNib() {
        super.awakeFromNib()

        // Handling press, longpress
        let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(bubbleTapped))
        self.bubbleButton.addGestureRecognizer(tapGestureRecognizer)
        let longPressGestureRecognizer = UILongPressGestureRecognizer(target: self, action: #selector(bubbleLongPressed))
        longPressGestureRecognizer.minimumPressDuration = 0.5
        self.addGestureRecognizer(longPressGestureRecognizer)


        self.bubbleButton.isUserInteractionEnabled = true
        self.isUserInteractionEnabled = true
    }


    @objc func bubbleTapped(sender: UITapGestureRecognizer) {
        delegate2?.bubbleTappedHandler(sender: sender)
    }

    @objc func bubbleLongPressed(sender: UILongPressGestureRecognizer) {
        delegate2?.bubbleLongPressHandler(sender: sender)
    }
}

protocol WorldMessageDelegate {
    func bubbleTappedHandler(sender: UITapGestureRecognizer)
    func bubbleLongPressHandler(sender: UILongPressGestureRecognizer)
}

The delegate was strong. After replcaing

var delegate2: WorldMessageDelegate?

with

weak var delegate2: WorldMessageDelegate?

And changing the protocol

protocol WorldMessageDelegate {

to

protocol WorldMessageDelegate: class {

Everything works fine.

May I be correct?

Kárpáti András
  • 1,221
  • 1
  • 16
  • 35
  • @ABeard89 what do you think? – Kárpáti András May 21 '18 at 09:18
  • 2
    You may be correct, yes. Having a strong reference to your delegate probably creates a strong reference cycle. Your viewcontroller holds a reference to the cell, via the tableview, and the cell holds a reference to the viewcontroller (if the viewcontroller acts as the cell's delegate). If both of these references are strong, the viewcontroller and the cell won't ever be released from memory. – rodskagg May 21 '18 at 09:20
  • @andlin thank you! Yes I understand it now, what a complicated thing is to make programs.. eh – Kárpáti András May 21 '18 at 09:40
  • That looks like it to me! I suspected a property needed to be changed to weak. – ABeard89 May 21 '18 at 10:08