【问题标题】:Why am I getting nil for tableview outlet?为什么我的 tableview 出口为零?
【发布时间】:2021-08-30 16:42:35
【问题描述】:

当 TaskListDataSource 的回调被调用时,它会重新加载 todayVC 和 reviewVC,因为它们是 UITableViewControllers。然而 plannerVC 不是,tableview 属性是一个出口。

@IBOutlet weak var tableView: UITableView!

为什么当回调运行时它崩溃说它是零。但是,如果我能够以某种方式在页面视图中滚动并查看 plannerVC,则它永远不会崩溃,因为 tableview 已加载到内存中。但为什么一开始不这样做呢?

override func viewDidLoad() {
        super.viewDidLoad()
        let taskListDataSource = TaskListDataSource {
            self.todayVC.tableView.reloadData()
            self.plannerVC.tableView.reloadData()
            self.reviewVC.tableView.reloadData()
        }
        todayVC = storyboard!.instantiateViewController(identifier: "TodayViewController", creator: { coder in
            return TodayViewController(coder: coder, taskListDataSource: taskListDataSource)
        })
        plannerVC = storyboard!.instantiateViewController(identifier: "PlannerViewController", creator: { coder in
            return PlannerViewController(coder: coder, taskListDataSource: taskListDataSource)
        }) 
        reviewVC = storyboard!.instantiateViewController(identifier: "ReviewViewController", creator: { coder in
            return ReviewViewController(coder: coder, taskListDataSource: taskListDataSource)
        })
        addVC = storyboard!.instantiateViewController(identifier: "AddViewController")
        setViewControllers([todayVC], direction: .forward, animated: false)
        dataSource = self
        print(plannerVC.tableView) // Console is printing nil
}

【问题讨论】:

  • 您正试图在 TaskListDataSource 的初始化中访问 self.plannerVC.tableView,但您的 plannerVC 被设置了两个语句之后
  • 即使您修复了 Alexander 所说的“访问顺序”,您也确实实例化了它们,但即使 IBOutlet 已在 Storyboard 中正确连接,它也尚未加载。您可以使用 plannerVC.loadView() 强制加载它
  • @Larme 我阅读了苹果提供的关于loadView() 函数的文档,第一行声明了You should never call this method directly.,所以我改为调用loadViewIfNeeded(),它已经解决了这个问题。这让人怀疑这是否是我面临的问题的唯一解决方案。
  • 确实,loadViewIfNeeded() 而不是loadView(),我的错。对于您的问题,您正在尝试访问尚未加载的 ViewController 的 IBOutlet。您可以稍后访问 IBOutlet,还是现在需要它?这是个大问题,这取决于你的架构......
  • @Larme 我有一个包含两个 UITableViewControllers 的 UIPageViewController(可以从这些控制器访问 tableview),然后我有一个带有 tableview 插座的 UIViewController。这些都在 UIPageViewController 的 viewDidLoad 函数中初始化。我在 UIPageController 中定义了一个闭包,它本质上是在发生更改时重新加载所有 tableviews - 这确保所有 UI 都是同步的。但它崩溃导致 UIViewController 中的 tableview 为 nil

标签: ios swift tableview


【解决方案1】:

当您调用instantiateInitialViewController(creator:) 时,UIViewController 会启动,但它的view(以及所有子视图,包括所有IBOutlet)不会加载到内存中。

因此,当您尝试执行 self.someIBoutlet 时(在您的情况下为 self.plannerVC.tableView.reloadData(),它会崩溃。

一种解决方案是使用loadViewIfNeeded() 强制加载视图。 由于加载视图可能很繁重,因此通常在 ViewController 不久之后显示时使用它(例如,在访问其中的插座的某些属性的didSet 中,因为它会在几秒钟内显示在屏幕上,所以无论如何,视图都会被加载,就在片刻之后)。

既然您正在加载 3 UIViewController,可能是您没有显示它们,而是过早地加载它们?
如果是这种情况,您可能会重新考虑您的应用架构(您的所有 UIViewController 都不需要在内存中进行初始化和加载,也不需要加载它们的视图)。
不过,您可以事先检查视图是否已加载,并且您可以使用isViewLoaded 访问插座。

我会在 PlannerVC 中为此添加一个方法:

func refreshData() { 
    guard isViewLoaded else { return } 
    tableView.reloadData()
}

旁注,它可能是一个协议(甚至更复杂,例如添加var tableView { get set },并具有refreshData() 的默认实现,但这更进一步,没有必要)...

protocol Refreshable {
    func refreshData()
}
let taskListDataSource = TaskListDataSource {
    self.todayVC.refreshData()
    self.plannerVC.refreshData()
    self.reviewVC.refreshData()
}

旁注,我会检查是否没有内存保留周期,我会在TaskListDataSource 的闭包中使用[weak self],并且还会使其成为VC 的属性。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-09-29
    • 2020-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-13
    相关资源
    最近更新 更多