Swift 5:为什么我的结构中的数据突然为空/零?

Swift 5: Why is my data within struct suddenly empty / nil?

更新:

我想知道的主要事情是,我能够在下载 JSON 功能成功后登录 self.schedule (.monday),但是当表应该是刷新了。

问题

目前在我的 ScheduleViewController 中有两个单独的 UITableViews,其中表格应该由视图控制器它的 downloadSchedule() 函数以不同的方式填充下载的数据。

URLSession 任务成功后,接收到的数据将存储在名为 json 的新变量中,然后在控制台中打印。一切正常。

现在,当 downloadSchedule() 函数完成它的请求并调用下一个重新加载表的函数时,我想计算 numberOfRowsInSection 的数据,但我得到一个 Thread 1: Fatal error: Unexpectedly found nil while unwrapping an optional value.

虽然这个变量之前是用下载的数据填充的,但是现在我要更新表格的时候突然空了。怎么会这样?

代码

我在 ScheduleViewController 中声明我的变量,如下所示:

var schedule: Schedule?

downloadSchedule()函数在viewDidLoad()之后执行:

func downloadSchedule(completed: @escaping () -> Void) {
    let url = URL(string: "example.com/meta/schedule")

    URLSession.shared.dataTask(with: url!) { data, _, error in
        if error == nil {
            do {
                self.schedule = try JSONDecoder().decode(Schedule.self, from: data!)
                DispatchQueue.main.async {
                    print("executed")
                    completed()
                }
            } catch {
                print("JSON Error")
            }
        }

    }.resume()
}

downloadSchedule {
    self.mondayTableView.reloadData()
    self.tuesdayTableView.reloadData()
}

首先更新表的示例:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // Return the number of items in the sample data structure.

    var count:Int?

    if tableView == self.mondayTableView {
        count = schedule?.monday.count
    }

    if tableView == self.tuesdayTableView {
        count =  schedule?.tuesday.count
    }

    return count!

}

这些是我的时间表和日期结构:

// MARK: - Schedule
struct Schedule: Codable {
    let monday: [Day]
    let tuesday: [Day]
    let wednesday: [Day]
    let thursday: [Day]
    let friday: [Day]
    let saturday: [Day]
    let sunday: [Day]
    let nextmonday: [Day]
    let nexttuesday: [Day]
    let nextwednesday: [Day]
    let nextthursday: [Day]
    let nextfriday: [Day]
    let nextsaturday: [Day]
    let nextsunday: [Day]
    let airtimeAPIVersion: String

    enum CodingKeys: String, CodingKey {
        case monday, tuesday, wednesday, thursday, friday, saturday, sunday, nextmonday, nexttuesday, nextwednesday, nextthursday, nextfriday, nextsaturday, nextsunday
        case airtimeAPIVersion = "AIRTIME_API_VERSION"
    }
}

// MARK: - Day
struct Day: Codable {
    let startTimestamp: String?
    let endTimestamp: String?
    let name: String?
    let dayDescription: String?
    let id: Int?
    let instanceID: Int?
    let instanceDescription: String?
    let record: Int?
    let url: String?
    let imagePath: String?
    let starts: String?
    let ends: String?

    enum CodingKeys: String, CodingKey {
        case startTimestamp = "start_timestamp"
        case endTimestamp = "end_timestamp"
        case name = "name"
        case dayDescription = "description"
        case id = "id"
        case instanceID = "instance_id"
        case instanceDescription = "instance_description"
        case record = "record"
        case url = "url"
        case imagePath = "image_path"
        case starts = "starts"
        case ends = "ends"
    }
}

JSON

{
  "monday": [
    {
      "start_timestamp": "2020-06-01 01:35:00",
      "end_timestamp": "2020-06-01 01:37:37",
      "name": "TEST",
      "description": "",
      "id": 174,
      "instance_id": 950,
      "instance_description": "",
      "record": 0,
      "url": "",
      "image_path": "example.com/api/show-logo?id=174",
      "starts": "2020-06-01 01:35:00",
      "ends": "2020-06-01 01:37:37"
    }
  ],
  "tuesday": [
    ...
  ]
}

您从未将任何内容分配给 self.schedule

这两行:

let json = try JSONDecoder().decode(Schedule.self, from: data!)
print(json)

不要更改 self.schedule 的值。你需要写

self.schedule = try JSONDecoder().decode(Schedule.self, from: data!)

当你这样做时:JSONDecoder().decode(Schedule.self, from: data!)

这意味着 "data is a bunch of bytes that are JSON, parse it into a Schedule value and return it."

在那一行中,Schedule.self 是我们试图将其解析为的类型——它与 self.schedule

无关

你不应该在 tableView:numberOfRowsInSection 方法中 return 潜在的 nil。因为它应该在初始 tableview 加载数据时调用,并且 100% 你会崩溃。您可以 return 0.

所以:

func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    // Return the number of items in the sample data structure.

    guard let schedule = schedule else { return 0 }

    var count:Int = 0

    if tableView == self.mondayTableView {
        count = schedule.monday.count
    }

    if tableView == self.tuesdayTableView {
        count = schedule.tuesday.count
    }

    return count

}

还有,你确定

  • schedule.monday. tuesday loadData 后不是 nil 吗?
  • self.scheduletableView:numberOfRowsInSection中是nil吗?
  • 这个 UITableViewDelegate 你没有超过 2 个 tableView(在这个 viewcontroller)?

如果您收到 url 请求或解析 json 的错误,您的 loadData 中的完成处理程序也会更新调用。将 completed() 添加到此位置或将您的位置移至底部:

func downloadSchedule(completed: @escaping () -> Void) {
    let url = URL(string: "https://api/....")

    URLSession.shared.dataTask(with: url!) { data, _, error in
        if error == nil,
            let data = data {
            do {
                self.schedule = try JSONDecoder().decode(Schedule.self, from: data)
                print("JSON \(self.schedule?.monday)")
            } catch {
                print("JSON Error")
            }
        }
        DispatchQueue.main.async {
            completed()
        }
    }.resume()
}