CollectionView 丢失对数据模型的引用

CollectionView losing reference to data model

几天来一直被同一个问题困扰。

我有一个 ViewControllerA class 作为 CollectionView 委托和数据源。 CollectionView 通过故事板连接起来。 ViewControllerA class 包含为 CollectionView 提供数据的数据模型。

每当 ViewControllerA 首次加载时,它都会连接到数据库,并根据需要将信息加载到数据模型 (userDictionary) 中。一旦控制器加载了所有数据,它就会重新加载 CollectionView,并触发侦听数据库中任何更改的同步方法,以相应地更新 userDictionary,并重新加载 collectionView 中的相应项目。

到目前为止一切正常。

当我转换到任何不同的 ViewController class 时,比如 ViewControllerB class,我正在传递来自 ViewController 的 userDictionary 副本A在prepareForSenderA中传给ViewControllerB,通过prepareForSenderB传回给ViewControllerA。

这是奇怪的行为。当我转换回 ViewControllerA class 时,CollectionView 可以很好地加载传递给 ViewControllerB 的相同数据,但无法加载同步方法在 ViewControllerA.

我知道同步方法仍然可以正常工作,因为当我在 ViewControllerA 中加载时打印出来的任何新数据都会显示在调试器中。而且我知道我在同一控制器 class 中的 userDictionary 数据模型正在接收这些更新,因为 didSet 观察器正在打印出 userDictionary 的最新状态。

奇怪的是,每当我在 ViewControllerA class 中打印出数据模型的内容时,它都会打印出模型通过时存在的旧状态至 ViewControllerB class。尽管 didSet 观察者只是证明了模型确实被更新了!

这几乎就好像 ViewControllerA class 以某种方式保留了对数据模型的引用,因为它在传递给 ViewControllerB 时存在,并且不知何故“丢失”当该模型被传回时,它引用“self”中的数据模型。

另一个注意事项:如果我一直停留在 ViewControllerA,并且不来回传递 userDictionary,我就不会再遇到这个问题了。

下面的代码总结了我如何来回传递数据:

视图控制器 A Class:

class ViewControllerA: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {

//MARK: Data Structures
var userDictionary = [[String: String]]() { didSet { print("userDictionary.count from DIDSET,", userDictionary.count)}}
//prints out: 9

//MARK: Initialization
override func viewDidLoad() {
    self.loadUserDictionaryFromDatabase()
}

func loadUserDictionaryFromDatabase() {
    //code that loads information from a database
    //var objectInstance["name"] = firstName
    //self.userDictionary.append(objectInstance)
    print("loader observed numberOfItems", numberOfItems)
    //...once data is fully loaded
    self.syncDataFromDatabase()
}

func syncDataFromDatabase() {
    //sync new data from database
    //var newObjectInstance["newName"] = newFirstName
    //self.userDictionary.append(newName)
    print("syncer observed newNumberOfItems", newNumberOfItems)
}

//MARK: View Controller Transitions
@IBAction func segueAtoB(_ sender: Any) {
    self.performSegue(withIdentifier: "segueAtoB", sender: self)
}

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

    //Segue from VCA to VCB
    if segue.identifier == "segueAtoB" {

        let controller = segue.destination as! ViewControllerA

        //Pass initialized userDictionary from VCA to VCB
        controller.userDictionary = self.userDictionary
    }
}

//MARK: Dummy Test Number of Items in UserDictionary
@IBAction func printMostUpToDateNumberofItemsInDictionary(_ sender: Any) {
    print("userDictionary.count from TEST,", userDictionary.count)
}

}

视图控制器 B Class:

class ViewControllerB: UIViewController {

//MARK: Data Structures
var userDictionary = [[String: String]]()

//MARK: View Controller Transitions
@IBAction func segueBtoA(_ sender: Any) {
    self.performSegue(withIdentifier: "segueBtoA", sender: self)
}


override func prepare(for segue: UIStoryboardSegue, sender: Any?) {

    //1. Segue to Home-Screen Controller
    if segue.identifier == "segueBtoA" {

        let controller = segue.destination as! ViewControllerB
        controller.userDictionary = self.userDictionary
    }
}

}

打印结果:

步骤 1: 在视图控制器 A 中启动

  1. loader 观察到 numberOfItems = 9
  2. userDictionary.count 来自 DIDSET,9
  3. syncer 观察到 newNumberOfItems = 1
  4. userDictionary.count 来自 DIDSET = 10 (10 = 9 + 1) -- PASS
  5. userDictionary.count 来自 TEST = 10 (10 = 9 + 1) -- PASS

第 2 步: 从 A 到 B 继续

viewControllerB.userDictArray = viewControllerA.userDictArray

第 3 步: 从 B 到 A 继续

viewControllerA.userDictArray = viewControllerB.userDictArray

步骤 4:观察调试器输出

  1. userDictionary.count 来自 DIDSET,10
  2. syncer 观察到 newNumberOfItems = 1
  3. userDictionary.count 来自 DIDSET = 11 (10 = 10 + 1) -- 通过
  4. userDictionary.count 来自测试 = 10 (10 != 10 + 1) -- 失败 (diSet 刚刚更新时,这条线是怎么出现的?userDictionary.count?)

使用 UUID 的调试器输出:

第 1 步:在视图控制器 A 中启动

  1. loader 观察到 numberOfItems,A74593E1-1231-41BE-A5DF-693591F998E4
  2. userDictionary.count 来自 DIDSET,A74593E1-1231-41BE-A5DF-693591F998E4
  3. syncer 观察到 newNumberOfItems,A74593E1-1231-41BE-A5DF-693591F998E4
  4. userDictionary.count 来自 DIDSET,A74593E1-1231-41BE-A5DF-693591F998E4
  5. userDictionary.count 来自测试,A74593E1-1231-41BE-A5DF-693591F998E4

第 4 步:观察调试器输出

  1. userDictionary.count 来自 DIDSET,A74593E1-1231-41BE-A5DF-693591F998E4
  2. syncer 观察到 newNumberOfItems,A74593E1-1231-41BE-A5DF-693591F998E4
  3. userDictionary.count 来自 DIDSET,A74593E1-1231-41BE-A5DF-693591F998E4
  4. userDictionary.count 来自测试,28D817D9-B53D-47D8-A3EF-2F7DDE6460FC

我真的认为你需要/想要使用 "unwind" segue。

ViewControllerA中添加这个函数:

@IBAction func returnFromB(_ segue: UIStoryboardSegue) {
    if let vcDest = segue.destination as? ViewControllerA,
        let vcSource = segue.source as? ViewControllerB {
            // pass userDictionary from VCB back to VCA
            vcDest.userDictionary = vcSource.userDictionary
        print("unwind segue from B")
    }
}

然后,通过按住 Ctrl 键将其从视图控制器图标拖动到情节提要中 ViewControllerA 顶部的退出图标。给它一个 segue 标识符。

现在,在ViewControllerB,您可以拨打:

self.performSegue(withIdentifier: "unwindSegueBtoA", sender: self)

ViewControllerA 中的 returnFromB() 函数将处理传回您的数据。

这是一个基本示例:https://github.com/DonMag/DataPass

另一个(可能更好)的选择是创建一个 "data manager" class 和 set/get 来自每个控制器的 class 的数据,而不是通过它来回。