首页 > 解决方案 > 在委托方法中引用时,弱 var 出口丢失(=nil)

问题描述

我的班级中有一个 UICollectionView 声明为 @IBOutlet weak var artifactsCollectionView: UICollectionView!

在这个类中,有一个委托方法被另外两个视图控制器调用,其中一个 VC 是弹出窗口,另一个是普通 VC。

委托方法从数据库中获取一些数据,然后更新在闭包内调用的集合视图:self.artworkCollectionView.reloadData()

当弹出 VC 调用委托方法时,一切正常。但是,当普通 VC 调用委托方法时,当它到达 self.artworkCollectionView.reloadData() 时,它会得到臭名昭著的致命错误:Unexpectedly found nil while implicitly unwrapping an Optional value。

我检查了所有对单元重用标识符的引用,一切都是正确的。我怀疑由于 UICollectionView 被声明为弱 var,当我从当前类转到弹出窗口然后弹出窗口调用委托方法时,引用并没有丢失,但是当我从当前类转到正常时VC,然后普通 VC 调用委托方法,对我的弱 var 的引用丢失了,因此它被“视为”为零。

@IBOutlet weak var artworkCollectionView: UICollectionView!

override func viewDidLoad() {
    super.viewDidLoad()
    // Set up
    artworkCollectionView.dataSource = self
    artworkCollectionView.delegate = self
    artworkCollectionView.isUserInteractionEnabled = true
    artworkCollectionView.allowsSelection = true
    artworkCollectionView.register(UINib(nibName: 
    "MyCollectionViewCell", bundle: nil), 
    forCellWithReuseIdentifier: "cell")
}


// delegate method
func reloadCollections() {

    retrieveAlbumRatings { (isAlbum) in
        if isAlbum {

            self.retrieveAlbumData(completion: { (isFinished) in

                if isFinished {
                    // Reload collection views
                    self.artworkCollectionView.reloadData()

                }
            })
        }
    }
}

如果我是对的,我的问题是:我怎样才能给 weak var artifactsCollectionView: UICollectionView!一个 STRONG 引用,这样它就不会在从当前类到普通 VC 并返回的流程中丢失?

编辑:这是我迄今为止尝试过的:

  1. 从出口声明中删除“弱”,使其成为:@IBOutlet var artCollectionView: UICollectionView!但我得到了同样的错误

  2. 我通过重写 performSegue 将 artifactCollectionView 传递给普通 VC,然后将其作为委托方法的参数传递回去。这不会给我致命的错误,但也不会重新加载 UICollectionView 因为我认为无论如何对 UICollectionView 插座的弱引用都会丢失。

感谢您的帮助(免责声明:我对 Swift 很陌生..)

标签: swift

解决方案


在这个类中,有一个委托方法被另外两个视图控制器调用,其中一个 VC 是弹出窗口,另一个是普通 VC。

委托方法从数据库中获取一些数据,然后更新在闭包内调用的集合视图:self.artworkCollectionView.reloadData()

  1. 流程似乎是您有一个包含上述代码的 VC,VC 可以打开一个弹出窗口,或者只是对“普通 VC”进行标准推送。
  2. 您希望在弹出 VC 或普通 VC 中发生一些操作,加载一些数据,然后当用户被引导回原始 VC 时,UICollectionView使用该数据更新。

您的问题如下:

我通过重写 performSegue 将 artifactCollectionView 传递给普通 VC,然后将其作为委托方法的参数传递回去。这不会给我致命的错误,但也不会重新加载 UICollectionView 因为我认为无论如何对 UICollectionView 插座的弱引用都会丢失。在大多数情况下,您不应该像这样传递任何东西,除非您有充分的理由这样做(我没有看到)。

你想在这里分离关注点。您必须仔细考虑要在 VC 之间传递的内容,以避免在它们之间产生奇怪的依赖关系。出于多种原因,我不会通过网点,首先是如果您决定更改它,您现在必须跟踪多个 VC 中的网点。第二个是它需要太多的心理体操来跟踪出口的状态,因为它被到处传递。网点也只能保证在生命周期的某些阶段设置。例如,如果您从 segue in 检索目标 VCprepareForSegue:sender:并尝试在那时引用 outlet,它们将全部为 nil,因为它们尚未设置。

这些都是为什么包含上述代码的 VC 应该是一个(也是唯一一个)保持对显示内容artworkCollectionView和时间的控制的充分理由。这里的问题是你如何处理这个问题,而不是让弹出窗口或普通 VC 调用委托方法或做一些奇怪的事情,比如将出口从一个 VC 传递到另一个,而是传递数据。

最简单的例子是:

  1. 弹出式 VC 和普通 VC 调用一些代码来实际获取数据。
  2. 然后根据您实际如何从原始 VC 转到弹出式 VC 或普通 VC,使用parentViewControllerpresentingViewController来获取对原始 VC 的引用。
  3. 通过该引用将数据设置到原始 VC 中。
  4. 如有必要,关闭弹出式 VC 或普通 VC(取决于您的特定应用程序,也许您希望用户按下UIButton关闭而不是为他们执行此操作)。
  5. 当原始 VC 重新出现时,将一些代码添加到生命周期方法中,例如 viewWillAppear让它将数据的内容加载到 UICollectionView那时。

我看不出你为什么应该通过原始 VC 之外的任何渠道,而这些渠道应该是管理它的渠道。


推荐阅读