【问题标题】:CollectionView losing reference to data modelCollectionView 失去对数据模型的引用
【发布时间】:2018-07-24 16:02:27
【问题描述】:

现在几天都在同一个问题上卡住了。

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

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

到目前为止一切正常。

当我转换到任何不同的 ViewController 类时,比如 ViewControllerB 类,我将 userDictionary 的副本从 ViewControllerA 传递到 prepareForSenderA 中的 ViewControllerB,然后通过 prepareForSenderB 将其传递回 ViewControllerA。

这是奇怪的行为。当我转换回 ViewControllerA 类时,CollectionView 可以很好地加载与传递给 ViewControllerB 的相同数据,但无法加载同步方法在 ViewControllerA 中观察到的任何新更改。

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

奇怪的是,每当我在 ViewControllerA 类中打印出数据模型的内容时,它都会打印出模型的旧状态,因为它在传递给 ViewControllerB 类时就存在。尽管 didSet 观察者只是证明了模型实际上已经更新了!

就好像 ViewControllerA 类以某种方式保留了对数据模型的引用,因为它在传递给 ViewControllerB 时存在,而当该模型被传回时,它以某种方式“丢失”了它对“self”中的数据模型的引用.

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

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

查看控制器 A 类:

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 类:

类 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 步: 在 View Controller A 中开始

  1. 加载程序观察到 numberOfItems = 9
  2. 来自 DIDSET 的 userDictionary.count,9
  3. 同步器观察到 newNumberOfItems = 1
  4. 来自 DIDSET 的 userDictionary.count = 10 (10 = 9 + 1) -- PASS
  5. userDictionary.count from TEST = 10 (10 = 9 + 1) -- PASS

第 2 步: 从 A 转到 B

viewControllerB.userDictArray = viewControllerA.userDictArray

第 3 步: 从 B 转到 A

viewControllerA.userDictArray = viewControllerB.userDictArray

第 4 步:观察调试器输出

  1. 来自 DIDSET 的 userDictionary.count,10
  2. 同步器观察到 newNumberOfItems = 1
  3. userDictionary.count from DIDSET = 11 (10 = 10 + 1) -- PASS
  4. userDictionary.count from TEST = 10 (10 != 10 + 1) -- 失败 (当 diSet 刚刚更新 userDictionary.count 时,这条线是如何发生的?)

使用 UUID 的调试器输出:

第 1 步:从视图控制器 A 开始

  1. 加载程序观察到 numberOfItems,A74593E1-1231-41BE-A5DF-693591F998E4
  2. 来自 DIDSET 的 userDictionary.count,A74593E1-1231-41BE-A5DF-693591F998E4
  3. 同步器观察到 newNumberOfItems,A74593E1-1231-41BE-A5DF-693591F998E4
  4. 来自 DIDSET 的 userDictionary.count,A74593E1-1231-41BE-A5DF-693591F998E4
  5. userDictionary.count 来自 TEST,A74593E1-1231-41BE-A5DF-693591F998E4

第 4 步:观察调试器输出

  1. 来自 DIDSET 的 userDictionary.count,A74593E1-1231-41BE-A5DF-693591F998E4
  2. 同步器观察到 newNumberOfItems,A74593E1-1231-41BE-A5DF-693591F998E4
  3. 来自 DIDSET 的 userDictionary.count,A74593E1-1231-41BE-A5DF-693591F998E4
  4. userDictionary.count 来自 TEST,28D817D9-B53D-47D8-A3EF-2F7DDE6460FC

【问题讨论】:

  • and passing it back to ViewControllerB through prepareForSenderB.: 你是使用segue返回还是关闭ViewControllerB?您可以通过在 ViewControllerA 的 viewDidLoad 中打印一行来检查它是否与您返回的 ViewControllerA 相同。 viewDidLoad 每个实例只加载一次,viewDidAppear 每次出现 ViewController 时都会发生。
  • 你不应该有一个“prepareForSenderB”...你是否使用导航控制器来“推”A -> B 和“弹出”B -> A?
  • @Purpose - “并通过 prepareForSenderB 将其传递回 ViewControllerB” - 错误地写为 ViewControllerB 而不是“A”。编辑我正在使用 segue 返回,而不是通过关闭 ViewControllerB,我确定我要返回 ViewControllerA...
  • 如果你可以调用dismiss,你为什么要创建一个新的ViewControllerA? viewDidLoad 中的打印行应该显示一个新的 VC 已实例化,所以我认为现在它看起来像 A-B-A 而不是 A-B 到 A。Passing data back
  • 感谢@Purpose。我确实对正在发生的事情有更清晰的了解

标签: ios swift collectionview


【解决方案1】:

我真的认为您需要/想要使用“放松”转场。

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-Drag 从 View Controller 图标连接到情节提要中 ViewControllerA 顶部的 Exit 图标。给它一个 segue 标识符。

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

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

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

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

另一个(可能更好)的选择是创建一个“数据管理器”类,并从每个控制器的该类中设置/获取您的数据,而不是来回传递它。

【讨论】:

  • 我认为如果我弄清楚如何自定义展开转场,展开转场应该可以工作......我也考虑过使用“数据管理器”类,但我的模型在在这一点上,重新架构需要付出相当大的努力,我想确定没有更简单的方法可以在不改变代码结构的情况下完成这项工作。
猜你喜欢
  • 2015-09-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多