如何用 Swift 处理空对象?

How to handle empty objects with Swift?

我有一个跟踪每月开支的应用程序。我有一个 Expense 实体,它有一个 month 属性,用于跟踪创建当前月份的费用。然后我会在 table 视图中显示每个月的费用,如此处所示。用户只有在下个月或上个月有消费时才可以左右切换

@IBAction func backMonthButtonPressed(sender: AnyObject) {
    print("Back Button Pressed")
    currentMonth = NSCalendar.currentCalendar().dateByAddingUnit(.Month, value: -1, toDate: currentMonth, options: [])!
    if checkMonth(currentMonth) {
        updateFetch()
        setMonth()
    } else {
        currentMonth = NSCalendar.currentCalendar().dateByAddingUnit(.Month, value: 1, toDate: currentMonth, options: [])!
    }
    tableView.reloadData()

}

@IBAction func nextMonthButtonPressed(sender: AnyObject) {

    print("Next Button Pressed")

    currentMonth = NSCalendar.currentCalendar().dateByAddingUnit(.Month, value: 1, toDate: currentMonth, options: [])!
    if checkMonth(currentMonth) {
        updateFetch()
        setMonth()
    } else {
        currentMonth = NSCalendar.currentCalendar().dateByAddingUnit(.Month, value: -1, toDate: currentMonth, options: [])!
    }
    tableView.reloadData()
}

func checkMonth(month : NSDate) -> Bool {
    let app = UIApplication.sharedApplication().delegate as! AppDelegate
    let context = app.managedObjectContext //scratch pad

    let fetchRequest = NSFetchRequest(entityName: "Expense")
    fetchRequest.predicate = NSPredicate(format: "month == %@", month.MonthYearDateFormatter())
    let count = context.countForFetchRequest(fetchRequest, error: nil)
    if count > 0 {
        print("There are expenses for this month \(month.MonthYearDateFormatter()). Show expenses")
        return true

    } else {
        print("There are no expenses for this month \(month.MonthYearDateFormatter()). Do Nothing")
        return false
    }
}

我的问题是,在不太可能发生的情况下,用户在 6 月创建了费用,但在 8 月没有创建费用。我怎样才能让用户在不跳过它的情况下仍然看到 8 月份的 his/her 费用。有任何想法吗?

我在细化之前做了一些优化:

@IBAction func backMonthButtonPressed(sender: AnyObject) {
    self.processMonth(step: -1)
}

@IBAction func nextMonthButtonPressed(sender: AnyObject) {
    self.processMonth(step: 1)
}

// a method uses almost the same code for both cases, so it was merged
func processMonth(step: Int) {
    let direction = (step < 1 ? "Back" : "Next")
    print("\(direction) Button Pressed")

    currentMonth = NSCalendar.currentCalendar().dateByAddingUnit(.Month, value: step, toDate: currentMonth, options: [])!

    //if checkMonth(currentMonth) {
    // I wouldn't test this because it locks you out from seeing empty month.
        updateFetch()
        setMonth()
    //}

    tableView.reloadData()
}

您所问问题的答案:

如果您的 UITableView 的数据源设置正确,您应该能够度过空白的月份

// changed return type from `Bool` to `void` as I suggested in the method not to test empty month, as it could be useless

func checkMonth(month : NSDate) {
    let app = UIApplication.sharedApplication().delegate as! AppDelegate
    let context = app.managedObjectContext //scratch pad

    let fetchRequest = NSFetchRequest(entityName: "Expense")
    fetchRequest.predicate = NSPredicate(format: "month == %@", month.MonthYearDateFormatter())
    let count = context.countForFetchRequest(fetchRequest, error: nil)
    if count > 0 {
        print("There are expenses for this month \(month.MonthYearDateFormatter()). Show expenses")

    } else {
        print("There are no expenses for this month \(month.MonthYearDateFormatter()). Do Nothing")
        // here you can make some additional actions, like seeting the empty table with "no expenses this month"
    }
}

无论如何,正如 @Paulw11 指出的那样,如果您的数据源不会耗尽大小,您宁愿从位于 viewDidLoad/viewDidAppear 的数据模型中获取数据示例,然后根据 currentMonth 变量(关于用户当前看到的月份)呈现每个月。

因此,您只能在控制器加载时以及每次用户更改当前月份视图时调用 setMonth() 方法。

在@Paulw11 和@pedrouan 的帮助下,我能够做我想做的事。

获取所有费用

    func fetchExpenses() {
    let app = UIApplication.sharedApplication().delegate as! AppDelegate
    let context = app.managedObjectContext //scratch pad
    let fetchRequest = NSFetchRequest(entityName: "Expense")

    //Always Sort Budget by the date it's created
    let sortDescriptor = NSSortDescriptor(key: "created", ascending: true)
    fetchRequest.sortDescriptors = [sortDescriptor]

    do {
        let results = try context.executeFetchRequest(fetchRequest)
        self.expenses = results as! [Expense]
    } catch let err as NSError {
        print(err.debugDescription)
    }
}

将约束设置为第一个月和最后一个月,以确定用户可以在 viewDidLoad 中浏览几个月的距离

    //If array is not empty, set first and last month
    if expenses.count > 0 {
        guard let first = expenses.first?.month else {
            return
        }
        firstMonth = first

        guard let last = expenses.last?.month else {
            return
        }
        lastMonth = last

    }else {
        //Set current month to be first and last
        firstMonth = currentMonth.MonthYearDateFormatter()
        lastMonth = currentMonth.MonthYearDateFormatter()
    }

限制用户超过第一个月和最后一个月

    @IBAction func backMonthButtonPressed(sender: AnyObject) {
    print("Back Button Pressed")
    if currentMonth.MonthYearDateFormatter() != firstMonth {
        self.processMonth(step: -1)
    }
}

@IBAction func nextMonthButtonPressed(sender: AnyObject) {

    print("Next Button Pressed")
    if currentMonth.MonthYearDateFormatter() !=  lastMonth {
        self.processMonth(step: 1)
    }
}

Return 当月的所有费用,如果有 none 清空 return 清空 table.

  func updateFetch() {
    setFetchResults()

    do {
        try self.fetchedResultsController.performFetch()
    } catch {
        let error = error as NSError
        print("\(error), \(error.userInfo)")
    }
}


func setFetchResults() {

    let app = UIApplication.sharedApplication().delegate as! AppDelegate
    let context = app.managedObjectContext //scratch pad

    //Fetch Request
    let fetchRequest = NSFetchRequest(entityName: "Expense")

    let sortDescriptor = NSSortDescriptor(key: "created", ascending: false)

    fetchRequest.predicate = NSPredicate(format: "month == %@", currentMonth.MonthYearDateFormatter())
    fetchRequest.sortDescriptors = [sortDescriptor]
    let count = context.countForFetchRequest(fetchRequest, error: nil)


    let controller = NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: ad.managedObjectContext , sectionNameKeyPath: section, cacheName: nil)

    //If there is none, empty fetch request
    if count == 0 {
        print("There are no expenses for this month. Show nothing")
        controller.fetchRequest.predicate = NSPredicate(value: false)
    } else {
        print("There are expenses for this month. Show expenses")
    }

    fetchedResultsController = controller
    controller.delegate = self
}

借助 pedroruan 的 processMonth(),我能够在空的 table 视图中导航,以便用户可以看到 8 月是空的,同时仍然能够转到 6 月。谢谢大家。